Table of Contents

 Team-Fly
Next Section
Java for S/390 and AS/400 COBOL Programmers
Java for S/390 and AS/400 COBOL Programmers
by Phil Coulthard and George Farr ISBN:1583470115
MC Press © 2000 (806 pages)

Teaches COBOL programmers how to read and write Java programs and helps them understand the concepts necessary to improve their Java skills.

Table of Contents
 | Java For S/390 and AS/400 COBOL Programmers
 | Foreword
 | Chapter 1 | - | The World of Java
 | Chapter 2 | - | Java Flow of Control
 | Chapter 3 | - | Java Syntax
 | Chapter 4 | - | Structured Operations and Statements
 | Chapter 5 | - | Data Types And Variables
 | Chapter 6 | - | Arrays,Vectors, And Hashtables
 | Chapter 7 | - | String Manipulation
 | Chapter 8 | - | Date And Time Manipulation
 | Chapter 9 | - | An Object Orientation
 | Chapter 10 | - | Exceptions
 | Chapter 11 | - | Threads
 | Chapter 12 | - | User Interface
 | Chapter 13 | - | Database Access
 | Chapter 14 | - | More Java
 | Appendix A | - | COBOL For OS/390 Flow Of Control
 | Appendix B | - | ILE COBOL Flow Of Control
 | Appendix C | - | Object-Oriented COBOL On OS/390
 | Appendix D | - | Using Java On OS/390
 | Appendix E | - | Using Java On OS/400
 | Appendix F | - | Software Loading Instructions
 | Index
 | List of Figures
 | List of Tables
 | List of Listings
Team-Fly Next Section


BackCover

 Team-Fly
Back Cover

Now COBOL programmers have a step-by-step guide to help them make the jump to Java. Java for S/390 and AS/400 COBOL Programmers teaches COBOL programmers how to read and write Java programs and helps them understand the concepts necessary to continue to improve their Java skills.

Rather than serve as a mere overview, Java for S/390 and AS/400 COBOL Programmers addresses Java’s syntax and much of the Java Development Kit's (JDK’s) functionality through comparisons with COBOL. It introduces applets, Java Native Interface (JNI), Remote Method Invocation (RMI), serialization, JavaBeans, Enterprise JavaBeans (EJBs), servlets, JavaServer Pages (JSP), and Extensible Markup Language (XML). Because VisualAge for Java (VAJ) is by far the Java tool most widely used by AS/400 and S/390 programmers, the book also includes an introduction to it with both AS/400 and S/390 details. The book even contains separate appendices with platform-specific information geared explicitly toward OS/400 and OS/390 readers.

Working with this book will help you to:

  • Read any Java program
  • Write, compile, and run Java programs
  • Deploy Java programs on the AS/400 and S/390 systems
  • Understand the basics of Java applications, applets, JavaBeans, servlets, and server pages
  • Understand options for accessing existing programs, data, and transactions on the AS/400 and S/390 systems
  • Know where to go to get additional information on any topic introduced in the book, including AS/400 and S/390-specific Java Web sites, Java- and CICS-specific Web sites, and more

About the Authors

Phil Coulthard joined the IBM Toronto Lab in 1986 and has specialized in tools for AS/400 programmers. He has worked as a developer and team leader on a number of products, including Sort; Character Graphics Utility; SDA; and his pet project, CODE/400, the Windows-based follow-on to Application Development ToolSet/400 (ADTS) that includes PDM, SEU, SDA, RLU, and DFU. Phil has been manager of many AS/400 application development products, and his current role is in strategy and architecture for all AS/400 application development languages (including COBOL) and tools (including VisualAge products as they relate to the AS/400). He works closely with his AS/400 and S/390 counterparts in Rochester, Toronto, and Santa Teresa in strategy and architecture development.

George Farr’s language focus brings him in constant communication with his AS/400 and S/390 COBOL and Java counterparts in Rochester, Toronto, and Santa Teresa. George joined the IBM Toronto Lab in 1985 and has specialized in the RPG language compilers. He has worked as a developer and team leader on all the RPG compilers on the AS/400, including RPG III, RPG IV, and VisualAge for RPG. He has also spent time working in an architecture and planning role for RPG. The last few years have seen George working as a manager of the VisualAge for RPG language team and the RPG IV language team.

Team-Fly


Java For S/390 and AS/400 COBOL Programmers

Phil Coulthard
George Farr

Published by IBM Press™
Program Director, IBM Centre for Advanced Studies: Gabriel Silberman
IBM Associate Publisher: Sheila Richardson
IBM Press Alliance Publisher: David Uptmor, IIR Publications, Inc.

For information on translations or book distributors outside the USA or to arrange bulk purchase discounts for sales promotions or premiums, please contact:

IIR Publications, Inc.
5650 El Camino Real, Suite 225 Carlsbad, CA
USA 92008-9711 (760) 931-8615

First edition
First printing: May 2000

ISBN: 1-58347-011-5

© 2000 by International Business Machines Corporation. Portions © 2000 by IIR Publications Inc.

Every attempt has been made to provide correct information. However, the publisher, the editors, and the authors do not guarantee the accuracy of the book and do not assume responsibility for information included in or omitted from it.

The following terms are trademarks of International Business Machines Corporation in the United States, other countries, or both: DB2 Universal Database, IBM, the IBM logo, IBM Press, MQSeries, SanFrancisco, SecureWay, VisualAge, and WebSphere. The e-business logo is a trademark of IBM and used under license.

Lotus is a trademark of Lotus Development Corporation in the United States, other countries, or both.

Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both.

Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. UNIX is a registered trademark in the United States and other countries licensed exclusively through The Open Group. Tivoli is a trademark of Tivoli Systems Inc. in the United States, other countries, or both.

Other company, product, and service names may be trademarks or service marks of others.

OS/400 V4R3 and higher
OS/390 V2R4 and higher

This book is dedicated to the three girls in my life, my lovely wife Christine and my two beautiful daughters Cassandra and Jennifer. Cassandra is on her way to becoming a dancer, and Jenny is on her way to having all her teeth. Chris is holding down four full-time jobs: her work, her kids, our home, and her husband. With the time writing a book demands, without their patience, no extracurricular activity such as this would be possible. Don't worry, I'll get that deck painted and the grass cut soon. I also would like to recognize the encouragement of my brothers Maurice ("Bud") and Wayne. They don't know COBOL or Java from cake and coffee, but they still feign an interest!

Phil Coulthard

To my lovely wife Diana. To my wonderful children: Angelica, my baseball "buddy" who keeps me updated on the scores. Michael, who always contributes to ensuring my wallet is empty. AnnaLisa, who is starting school this year! In addition, I would like to dedicate this book to my sweet and beautiful sisters: Munira, Basima, and Ghaida. They never fail giving their younger brother good "motherly" advices!

George Farr

ACKNOWLEDGMENTS

We heartily thank the following fellow IBMers who took personal time to review the book or to answer our endless questions:

  • Gretchen Hargis (We are especially indebted to Gretchen's help.)

  • Chris Tandy

  • Sally Howard

  • Tom Ellis

  • Larry England

  • Mark Cathcart

  • Dan Rhoads

  • Al Shannon

  • Ann Wallace

  • Don Wright

  • Tom Ross

  • Dale Booth

  • Barbara Meyers

  • Clifton Nock

  • Richard Dettinger

  • Mike Fulton

  • Les Png

  • Jon Paris

  • Susan Gantner

In addition, we wish to thank the following customers who also took an active interest in both the topic and the book:

  • Stephen Gordon

  • Roger Pence

Finally, we wish to thank Sheila Richardson of the IBM Center for Advanced Studies for making this book and its precursor possible, and a reality. We appreciate her considerable help (again) in the logistics of bringing a book to market.

About The Authors

Phil Coulthard joined the IBM Toronto Laboratory in 1986 and has specialized in tools for OS/400 programmer (and System/36 before that). He has worked as a developer and team leader on a number of products, including Sort, Character Graphics Utility, Screen Design Aid, and his pet project, CODE/400, the Windows-based follow-on to ADTS (Application Development ToolSet, which includes PDM, SEU, SDA, RLU, and DFU). He has been manager of many OS/400 application development products, and his current role as of this writing is in architecture and strategy for many OS/400 AD languages (including COBOL) and tools, including VisualAge, as they relate to OS/400. He works closely with his OS/400 and OS/390 counterparts in Rochester, Toronto, and Santa Teresa in strategy and architecture development.

Phil has a specialized honors degree in mathematics and computer science from the University of Toronto. He is a frequent speaker at COMMON and local user groups, and a frequent author of articles in industry and user group magazines. He also works closely with AS/400 customers on architecture, strategy, and tooling decisions for the future. Phil can be reached at coulthar@ca.ibm.com.

George Farr joined the IBM Toronto Laboratory in 1985 and has specialized in the RPG language compilers. He has worked as a developer and team leader on all the RPG compilers on OS/400, including RPG III, RPG IV, and VisualAge for RPG. He has also worked in an architecture and planning role for RPG. The last few years have seen George working as manager of the VisualAge for RPG language team and the RPG IV language team. George's language focus brings him in constant communications with his AS/400 and S/390 COBOL and Java counterparts in Rochester, Toronto, and Santa Teresa.

George has a specialized honors degree in computer science from York University in To-ronto. He is a frequent and very popular speaker at COMMON and local user groups, and a frequent author of articles in industry and user group magazines. George is also the co-author of the books ILE: A First Look and RPG IV by Example. George can be reached at farr@ca.ibm.com

Phil Coulthard and George Farr are also co-authors of the book Java for RPG Programmers, available at www.mc-store.com.


Foreword

FROM MARK CATHCART,
IBM S/390 SOFTWARE STRATEGY:

The addition of Java to OS/390 and VM/ESA mainframe operating systems an- nounced the arrival of Java as a mainstream programming language. With Java® for S/390 and AS/400 COBOL Programmers, Phil Coulthard and George Farr have taken this to the next level by providing the definitive guide to help COBOL programmers make the jump to Java.

Over the coming months and years, Java will make even further in-roads into traditional programming environments. Already, you can use Java as a programming language for CICS transactions; using Java for IMS transactions and DB2 Stored Procedures is just around the corner. The ability to write common, portable, business components and functions that can be shared between these environments, and other mainframe systems, and re-used in applications running on such diverse systems as mobile devices, laptop computers, Personal Computers PCs, and UNIX-based workstations, will mean greater productivity and combined with the e-business delivery model, make Java even more compelling.

We expect to make significant breakthroughs in Java performance through the design and implementation of a new, much more scaleable Java Virtual Machine for OS/390 and the integration of the JVM into existing OS/390 Application Servers. This integration will enable many new programmers to design and develop programs written in Java and exploit the power, scalability, security, and reliability of OS/390. What they need is to understand how to work in an enterprise environment and how to use and reuse existing business applications.

Java for S/390 and AS/400 COBOL Programmers will help you get started, help you understand the differences between COBOL and Java, and help you to use Java to capitalize on your experience in writing business-critical, enterprise-ready, applications.

Mark Cathcart, IBM S/390 Software Strategy

(See Mark Cathcart's Web site, "Cathcart's Corner," dedicated to S/390 and emerging technologies, at www.ibm.com/s390/corner.)

From Roger Pence,
AS/400 Consultant and Industry Analyst,
Yellow Creek Data Design

AS/400 application development is currently experiencing a revolution. The pervasiveness of the Web and the Internet is quickly changing traditional views of what AS/400 application development is. For many AS/400 shops today, the line between "traditional" application development and Web/Internet development is quickly fading.

Java is at the core of this AS/400 application development revolution. Not only does Java effectively Web-enable OS/400, but Java also steps in to remedy the long-standing limitations of OS/400's traditional green-screen user interface. Virtually all AS/400 programmers today are aware of IBM's commitment to Java, and how important Java is to the future of the AS/400 and to their own careers.

With Java® for S/390 and AS/400 COBOL Programmers, Phil Coulthard and George Farr couple their real-world knowledge of COBOL with their intimate understanding of Java to create the Java handbook for COBOL programmers. Dispensing with hype and hoopla, Phil and George effectively and rationally provide the information you need to quickly get Java under your belt. Although Phil and George carefully explain Java's most mysterious aspects, they do so with a casual, simple grace that challenges the conventional wisdom that Java is hard for procedural programmers to learn. For example, OO programming details are effectively deferred until chapter 9, with an unthreatening, "Oh, yes, and here's what you've been learning" demeanor.

This isn't the only book COBOL programmers will need to learn everything there is to know about Java, but it is the first book COBOL programmers need to start learning Java. Dig in-the AS/ application development revolution won't wait for you!

Roger Pence

AS/400 Consultant and Industry Analyst
Yellow Creek Data Design


Chapter 1: The World of Java

Overview

What is Java? We have good news for you: Java is, after all, just another programming language that you can understand and master. Java is popular now, and we predict that its popularity will grow. Chances are that Java is in your future. Congratulations on taking the first concrete step on your journey to the brave new world of Java. Don't worry, this world is round too!

Will this be the first and last book you read on Java? Probably not. Once you get started and begin to write your own programs, you'll probably want to learn about more specific applications of the language. This book gives you a solid foundation in the structure, functions, and features of the language. On the strength of this introduction, you will be able to delve deeper into the language with other books.

Java is much different than COBOL. But so is programming your VCR, and you mastered that, didn't you? This book introduces you to the language from an OS/390 and OS/400 perspective, building the bridges of comparison for you, and using terms you are familiar with. We leverage your existing skill set to help you build a new one.

Java is an object-oriented language. It is inconceivable that a new language today would not be object-oriented; the benefits in productivity and quality are too great to ignore. These gains come from the capacity to reuse code and perform better problem domain modeling. Somebody on your team is going to have to become very good at doing object-oriented analysis and design in order to write efficient, high-quality Java code. Wish him or her well. The good news is that not everyone on the team needs to become an expert at OO to work effectively with Java. Although this book does not examine object-oriented programming in any great depth, it does give you the basic concepts and techniques you'll need to grasp the subject. After that, experience, battle scars, and further research will round out your capabilities.

If Java is just another programming language, then why all the fuss? There are a number of reasons:

  • The name. "Java" is a cool name! Programmers like that.

  • The Internet. Java's original reason for existence is the Internet. Programmers like that.

  • Portability. With Java, it's possible to write completely portable code. Programmers like that.

  • Accessibility. Java is free. Programmers like that.

The following sections examine these reasons a little further. We hope you like that.


The Name

Java was originally designed by engineers at Sun Microsystems, Inc. The intent was to create a small-scale, interpreted language for programming small consumer devices. Reportedly known as Oak in its early days, it was originally an embedded language that quickly found a new use in Internet-related programming. Java had all the attributes a programming language for the Internet needed: it was small, simple, easy to code, platform-independent, and supported dynamic loading of code on demand. Thus, it was renamed and made publicly available as a programming language for the Internet in 1995.

Java's big boost came in early 1996, when Netscape Communications Corporation stepped up to support it as a programming language that could be processed by its Web browser. Suddenly, thousands of programmers and Web site owners wanted to learn and use Java.

What about the name? Well, can you imagine a developer in the ultra-cool world of UNIX and the Internet working with a hot, new language called Oak? Apparently, neither could the engineers at Sun. Legend has it that the name came from the coffee they drank in vast quantities while working so hard on the project.


The Internet

The original reason for Java's popularity, as we stated, was the Internet-specifically, the ability to program Web pages displayed by Web browsers such as Netscape Navigator or Microsoft Internet Explorer. Web browsers display text and graphics through HTML (Hypertext Markup Language), which is a tag-based language interpreted by Web browsers.

Java adds the ability to embed Java applets inside HTML pages. Applets are small applications written in Java that run only inside Web browsers, alongside the HTML. Applets have the full power of a modern programming language behind them, so fully interactive, animated, live-data Web pages are made possible beyond what HTML could originally offer. Java can also be used to author Servlets, which are server-side programs for serving up Web pages dynamically and on demand.

Java also supports easy Internet communications programming. It is possible to write a Web browser entirely in Java that performs the basic tasks of finding, retrieving, and displaying HTML files. Sun has done just that with its HotJava Web browser.


Portability

Java approaches more closely than any preceding language that elusive goal of "write once, run anywhere." It is possible to write completely portable applications in Java without too much sacrifice or pain. Here is how:

  • Java (like Basic and Smalltalk) is interpreted rather than compiled.

  • Java's interpreter is everywhere, in operating systems, Web browsers, personal devices, and chips.

  • Java is rich in functionality, so there is little need for operating-system APIs.


Accessibility

Sun Microsystems made Java available for free over the Internet for Windows and Solaris. Since then, IBM and others have offered it for free on their operating systems. Not only the runtime version of Java is free, but also the actual development toolkit.

Visit any Web-search site and do a search for the word Java. You will find thousands of free Java tutorials, newsgroups, and other Java resources at your disposal. Of course, you can pay for onsite education and robust development tools, but one of Java's most alluring features is that you do not have to make that investment.

Check out your local college. It will offer a Java programming course. They all do. (Finally, the Pascal courses are being retired!)


Roles Of Java

Java can play a role in at least three important places in your applications:

  • The client. This can be a traditional application, a Web browser-based applet, or a Java Servlet-created Web page. The user interface runs on any client connected to your server, including Windows, UNIX, and Linux.

  • The server. Java is a competent business-application language on OS/390 and OS/400, enabling the writing of business logic traditionally authored in COBOL.

  • The glue. Whatever the decision for the client and whatever the language of the server, Java is a good option for tying the two together.


House Of Java

Java comes in its purest form as a Java Development Kit, or JDK. This kit contains a number of fundamentals, including these:

  • A Java compiler that compiles Java source code into efficiently interpreted bytecode (as opposed to directly executable machine code). This is the javac command.

  • A Java interpreter that interprets or runs Java bytecode. This is the java command.

  • A set of Java class libraries or packages for often-needed programming tasks.

These are the basic elements that define the language, together with their documentation. The documentation is in HTML and is shipped compressed into a file that is a separate download from the JDK, so be sure to get it too. While packaged separately, the documentation is still considered part of the JDK.

The JDK also includes a number of other useful command-line tools. However, it does not include an editor, an integrated development environment, or a visual design tool. And the JDK provides only the most rudimentary of debuggers. Nonetheless, with a decent PC editor (such as the editor in VisualAge for COBOL or CODE/400 for Windows), you have all you need for basic Java development. Figure 1.1 illustrates the pieces of the Java Development Kit.

Figure 1.1: The Java Development Kit (JDK).

Most programmers, however, will opt for any one of the numerous full-scale Java development tools that are available, such as IBM's VisualAge for Java, Inprise's JBuilder, or Symantec's Visual Cafe. These start with the basic JDK and add IDEs (integrated development envi-ronments), class hierarchy browsers, debuggers, visual design tools, wizards, and many more items to make the Java development task easier. Multiple-developer shops should also look for tools that offer built-in team support and versioning control. These are, again, covered by IBM's VisualAge for Java. IBM's VisualAge for Java product is also unique in that it offers built-in special support for both OS/390 Java and OS/400 Java.

New versions of the JDK originate with Sun. Based on tremendous programmer feedback, the company adds enhancements and features to the language and, once or twice a year, releases a new version (such as 1.2). In between versions, Sun issues bug-fix releases (such as 1.2.2); these appear several times each year. Other industry players betting heavily on Java (such as IBM, Oracle, and Netscape) work with Sun to define and even implement some specialized pieces.

In February, 1997, Sun released 1.1.0 of the JDK, and quickly followed up with several quality-improvement releases (up to 1.1.8 as of this writing). Version 1.1 was a majordelta improvement over its predecessor, 1.0. In December 1998, Sun released version 1.2 (although branded as "Java 2"), which is yet another major advance of the language, demonstrating that the JDK is maturing and improving very quickly. As of this writing, Java 2 is up to release 1.2.2, and 1.3.0 is in beta test.

Note

Because this book examines the basics of Java, all our samples were written using Sun's JDK on Windows NT. We happen to use VisualAge for Java for entering the source, but you, of course, can use whatever tool you have handy, including the Notepad word processor that comes with Windows. By the way, the COBOL code written for this book was written using the Windows-based editor in VisualAge for COBOL and CODE/400, tools we highly recommend for high productivity for OS/390 and OS/400 COBOL development, respectively.

Sun makes its JDK publicly available for both the Windows (32-bit) and Solaris "Java 2 SDK"operating systems at its Web site, www.java.sun.com/products (follow the link for JDK or SDK). When you install it, be sure to follow the instructions in the Readme file, especially those related to updating your PATH and CLASSPATH statements in Windows.

Other operating-system vendors license the JDK code from Sun and port it, as-is, to their own operating systems. For example, the IBM laboratory in Hursley, England licenses and ports the JDK to the IBM operating systems AIX, OS/2, OS/400, OS/390, VM, and even Linux. For each IBM operating system, that system's IBM team leverages the ported base and then integrates it deeply into the operating system for improved performance. Various Web-browser manufacturers also license the interpreter part of the JDK code in order to run Java applets embedded in Web pages.

As part of that license, the licensee agrees to stay current with the JDK, and must port a new release of the JDK within about six months of release. This ensures a reasonably current base across all the operating systems and Web browsers, and ensures user-program portability. The JDK now comes with most, if not all, major operating systems. It is available for all versions of OS/390 (but the later the version, the better the performance), and with OS/400 as of version 4, release 2 (V4R2). For details, see the Web pages www.ibm.com/s390/java and www.ibm.com/as400/java.

You might get confused about who actually owns Java: Sun Microsystems or JavaSoft? The answer is both. Sun is the parent company, and JavaSoft is a child company. Ultimately, Sun is the owner.


Java Bytecode

Java achieves its portability because of its bytecode architecture. As illustrated in Figure 1.2, the Java source code you enter is compiled into bytecode using the JDK-supplied javac command-line compiler. This compiler is really only the front half of a traditional compiler, in that an executable program is not produced as a result. Rather, it stops at what is referred to as the intermediate code in traditional compilers. Compared to reading the source, this is an efficient, compact version of the source, which is still independent of any particular operating system.

Click To expand
Figure 1.2: Compiling Java source code to Java bytecode.

The Java source code is contained in files that end with the extension .java, ("dot java"), while the Java bytecode is contained in files that end with the extension .class ("dot class").

You can send a bytecode .class file to any operating system or Web browser that contains the Java interpreter, and run it there without change. The hard work is performed by the interpreter itself-it has to be ported. The operating system and Web browser vendors do this hard work for you.

Note

The interpreter does occasionally have to call the operating system to run your Java instructions. For example, you might have a Java instruction to list the files in a directory. This will be captured in the bytecode using its predefined operators and operands. When the interpreter runs these instructions, it calls the appropriate operating-system API to retrieve that list.

In order to run a Java application, you invoke the java command and pass it the name of your bytecode .class file as a parameter, as illustrated in Figure 1.3. For applets, you need to use something called an APPLET tag in your Web-page source. We will describe this shortly.

Click To expand
Figure 1.3: Running a Java application.
Note

Sun and other operating-system vendors supply a subset of the JDK called the Java Runtime Environment, or JRE, for the operating systems they support. The JRE consists of only the Java virtual machine, or JVM (i.e., the interpreter), and the supporting runtime packages. As such, the JRE is intended as a distribution option for your applications if you do not want to assume the existence of a virtual machine on your users' computers, or if you are worried that their versions of the virtual machine might not match yours.


Java: The Language

Java is based to some extent on C++, Smalltalk, Ada, and other languages. It is most heavily influenced by C and C++, as anyone with a background in these languages will immediately recognize. It captures the best of these languages, while adding and subtracting functions to achieve robust functionality and easier programmability.

The working theme of the Java designers was, and is, KISS ("keep it simple, stupid"). To that end, the language does not have the following:

  • Pointer variables. (It does, however, have object references, as you will see.)

  • Memory allocation and deallocation constructs.

  • Include files, which are called "copy books" in COBOL parlance.

  • Make files (a language for facilitating C and C++ builds).

  • Multiple inheritance (an object-oriented construct that C++ uses).

  • Method prototyping. In Java, you declare and define methods at the same time.

  • Operator overloading, a complex function available in C++.

The biggest plus, from the point of view of programmer productivity, is the absence of memory-management functions. Instead of supplying built-in functions for getting and returning memory, Java offers automatic garbage collection. That is, when you declare variables, Java keeps track of where they are used at runtime, and frees up their memory when it determines your code no longer requires them. If you have written any C or C++ code, then you know that you must take care to do the following:

  • Allocate enough memory.

  • Reallocate the memory when more is needed.

  • Free up the memory when done with it.

  • Free up the memory once and only once.

Memory management is very error-prone, and such errors are typically hard to reproduce and difficult to find. Garbage collection is a language feature Java picked up from Smalltalk, and one that, by itself, saves a significant amount of programmer time. (Now just imagine if someone could manage your real memory! You would save even more time! Maybe in the next release.)

In Java terms, KISS could also be interpreted to mean "keep it safe, stupid"-in addition to focusing on ease of use, Java also focuses on security. An Internet language must be secure, as it is not considered polite for code downloaded to client workstations to have the potential for obliterating the data maintained on those trusting, friendly little computers.

Java's extensive built-in security is designed to prevent problems that might arise from both malicious intent and poor programming. The most fundamental of these possibilities is the decision to not define pointers in the language. While this might be seen as a programmer plus (pointer programming, after all, is a battle-won skill), it also stands as a major sand trap for bad-boy hackers. Many malicious programs are written by accessing memory that the program is not supposed to utilize, which is done by clever manipulation of pointers. We could tell you more about this but then, of course, we would be creating a security problem ourselves. Suffice it to say, "see no pointer, do no evil."


The Internet, Html, And Java Applets

As mentioned earlier, Java's initial role was that of a Web browser language. In this form, it is used to create applets that run inside Web browsers.

Applets resemble traditional applications, except that they are embedded inside HTML and share the Web browser's user-interface real estate. HTML stands for HyperText Markup Language, a tag-based language for formatting text, images, and other content that is intended to be displayed by Web browsers.

A tag has both a starting form and an ending form, and the text for that particular tag goes between them. In HTML, a tag is enclosed in angle brackets. An end tag is prefixed by a slash (/). For example, an unordered list is started in HTML by <ul>, and ended with </ul>. In general, the tags are short forms of the English word the tag represents, such as <li> for "list item," <p> for "paragraph," and <h1> for "heading one."

HTML includes many tags for partitioning and formatting text. For example, you can use tags for such things as creating headings, paragraphs, and lists, as shown in Listing 1.1.

Listing 1.1: An example of HTML tags.
Start example
<html>
<head>
<title>My First Web Page</title>
</head>
<body>
<h1>This is a header</h1>
<hr>
<p>
This is a paragraph. Well, you see how simple it is! The following is
an ordered list...
</p>
<ol>
<li>Type in the starting tag inside angle brackets
<li>Type in the body between the tags
<li>Type in the ending tag with a slash in front
</ol>
</body>
</html>
End example
Note

HTML tags are not case-sensitive. Also, some tags, like <hr> (horizontal rule) do not have an ending tag. Others, like <li> (list item), have an optional ending tag; the end is implied by the existence of the next tag.

Figure 1.4 shows how the HTML from Listing 1.1 appears in Netscape Navigator. Note that HTML is not compiled. You simply create the HTML source with a text editor, save the file with the filename extension .htm or .html, and then open the source document directly with your Web browser. The Web browser interprets the HTML content on the fly and displays it according to the instructions provided by the tags.

Click To expand
Figure 1.4: Netscape Navigator showing an HTML page.

HTML provides plenty of tags to spice up your Web pages, including those for formatting fonts, graphics, and even audio. One all-important tag is used to link a phrase or image with an element elsewhere on the same page, or on another Web page. The text or image that represents the link is highlighted in several ways, depending on how the user has configured the browser. Most often, a textual link is displayed in the browser window in a different color than the rest of the text, and is underlined as well. Images that function as links are usually surrounded by a border. When you pass your cursor over the link, the cursor turns into a little hand. Clicking the mouse while over the link takes you to the referenced Web page. That is, the content of the Web browser's window changes in order to display the referenced page. Clicking on the left arrow in the toolbar of the browser returns you to the originating page.

The ability to jump quickly from one location to another is what puts the hyper in hyper-text. Using hypertext, you can author a complex web of pages that are easily traversed by the end user or reader. An example of HTML that includes hypertext is shown in Listing 1.2. Figure 1.5 shows the result, as displayed in Netscape Navigator.

Click To expand
Figure 1.5: Netscape Navigator displaying a hypertext link.
Listing 1.2: An example of HTML that provides the ability to jump from one location to another.
Start example
<html>
<head>
<title>
My Second Web Page
</title>
</head>
<body>
<p>
To see my first web page, click
<a href="HTML1.HTM">here</a>
</p>
</body>
</html>
End example

Authoring traditional, static Web pages is quite simple; all you need is an editor, an HTML reference manual, and a Web browser. All of these are available for free on the Internet today. However, the example shown in Listing 1.2 and Figure 1.5 presents a link to a local file on your hard disk. You have not made a link to a location on the Internet yet. To do so, you need to connect your computer to the Internet. Often, this is done by paying a monthly access fee to an Internet Service Provider (ISP).

Along with giving you access to the Internet, it's common for an ISP to allocate space where you can install your HTML source files on the ISP's server. You then use the server's HTTP (HyperText Transport Protocol) Server software to make your HTML files accessible from the Internet. An HTTP Server serves up Web pages to Web browsers. HTTP Server software is often referred to as a Web server.

Your HTML files, graphics files, and other contents are each assigned a universal, unique URL (Uniform Resource Locator), or address. These addresses enable your Web pages to be found by Internet surfers. Surfers can display your page either by typing in its URL in their Web browser window, or by following a hypertext link as described above.

The important thing to remember about these links is that they can refer to any HTML file anywhere else in the world, as long as that file's URL is known. A URL consists of several parts:

  • The protocol or prefix section of the URL denotes the part of the Internet on which the file resides. Web pages use the HTTP protocol, which corresponds to the prefix http://, whereas file:/// is used to identify local files.

  • The protocol is followed by the domain name for the HTTP Server, such as www.ibm.com.

  • The domain name is followed by the pathname section. This includes a series of file directories, separated by slashes, and ending in the actual filename of the Web page, graphic image, or other object to be displayed by the browser.

URLs can refer to many types of resources accessible through the Internet, in addition to HTTP files and local files. The tip to the Web browser as to what follows is the first part of the URL name, which designates the protocol being used to access the file.

Let's look at an example:

http://www.ibm.com/software/ad/vajava.html

This is the name of an Internet file vajava.html, in directory software/ad, at HTTP Server domain www.ibm.com -IBM's main Web site.

How did IBM get its domain name, www.ibm.com? IBM applied for it, from a regulating body that hands out unique names. Currently, that regulating body is called InterNIC or Internet Naming Information Center. InterNIC owns the job of assigning unique Web names upon request. Often, InterNIC assigns blocks of unique names to Internet providers who, in turn, sell individual names to their customers who wish to set up their own Web sites. Large companies apply to InterNIC directly to get their own blocks of names. For more information, see www.internic.net.

Note

The infrastructure and underpinnings of the Internet are beyond the scope of this book. Many good books on the market describe this, and even your operating system's TCP/IP manual can be a big help.

HTML as an Internet User-Interface Language

Choosing a Web-browser interface for your applications is a good idea. With such an interface, your internal applications, such as accounts-receivable files, can be served on an intranet that only employees of your company can access (and possibly only restricted employees within your company). Your external applications, such as product-ordering forms, can be made available to the Internet world at large.

Such filtering of access between external Internet files and internal intranet files can be accomplished with a security device called an Internet firewall. A firewall is software or hardware that restricts access to a Web site to Web surfers with a particular computer IP address or within a particular block of IP addresses, or by means of passwords.

Regardless of whether you choose intranet or Internet deployment for your files, the bulk of your application runs on the server. Typically, only the user interface part runs on the client. Furthermore, even when the code runs on the client, it comes from the server on demand. It is sent to the Web browser by your HTTP Server software. This architecture offers a number of advantages over the traditional client/server option for adding graphical user interfaces to your applications:

  • It supports all clients, not just Windows.

  • It eliminates the need to install and maintain your programs on every user's workstation.

  • It leverages your server's scalability, security, backup infrastructure, and performance.

  • It expands your application's reach to potentially anyone with a Web browser.

We referred to these Web pages as static. That means they only display information to the user; they do not accept input from the user. This is unacceptable for most applications. There is a way, using conventional HTML, to define interactive Web pages. This involves special HTML tags for defining forms, which allow you to create input-capable fields, such as text entry boxes and drop-down lists. You will see an example of a form in chapter 14.

The following process illustrates how forms are commonly used to receive data entered by a user and process that data on the server side:

  1. The HTML form defines fields that ask the user for some type of input.

  2. The HTML form contains a Submit button that is associated with a server-side program.

  3. When the user presses the Submit button, the user input is sent to the server-side program.

  4. The server-side program uses APIs to retrieve and process the user input.

  5. The server-side program uses APIs to create a new Web page that is displayed to the user.

The server-side program used to process HTML form input is called CGI, or Common Gateway Interface. CGI is the standard protocol between the Web browser and the HTTP Server for processing forms. CGI was, prior to Java, the only way to enable users to inter-act with your Web pages. It is a mature and pervasive technique for producing interactive browser-based interfaces. If you have ever registered a software purchase online (that is, via the Internet), chances are you have used such a program to get your purchase information to the vendor.

Various languages are available for writing the server-side program, depending on the Web server (that is, the HTTP Server) software package you are using, and on the server itself. You can also use a general scripting language called Perl. Perl is an interpreted language that works well for writing CGI programs. It is available as a freely downloadable language on all major operating systems, including the OS/390 and OS/400. Perl is quite an arcane language to learn, but it is available everywhere, and is always freely downloadable from the Web. Perl comes from the world of shareware-UNIX. To find out more, see the Web site www.perl.com.

Do you have to learn CGI programming techniques to write interactive Web pages? Not anymore. There is a new option called Java Servlets. This standard was originally defined and created by Sun, but has rapidly been adopted by all major Web servers, including IBM's. You can even use plug-ins to enable Servlets in those Web servers that don't support them yet. For example, IBM's WebSphere Application Server can work with all the major Web servers, including Microsoft Internet Information Server (IIS).

Servlets are just like CGI programs. They are called by the HTTP server when the user presses a Submit button, they process input from HTML forms, and they produce a new page to be displayed in the browser. However, they are written in Java and use specific Java-supplied APIs to process input and prepare output. They are only loaded into memory once, and remain in memory until a preconfigured time. CGI, on the other hand, starts another job for each call to a CGI program. This means Servlets offer the programming, security, and stability advantages of Java, are more efficient than calling external CGI programs, and easily support state information between client invocations.

Servlets are exploding in popularity, especially among OS/390 and OS/400 programmers used to the screen-at-a-time user-interface model that HTML offers. You'll see examples of Servlets in chapter 14.

Java Applets

So how else does Java fit into this picture? By extending the HTML language with a new tag, <APPLET>. This tag refers to a Java program (an applet) to be run by the browser when the tag is encountered while reading the HTML. Applets most often contain user interfaces, although they could as easily be raw logic, such as a tax calculation applet. If they do have user interfaces, they have the full power of Java's very rich graphical-user-interface (GUI) library at their disposal. For example, they might show a multiple-column scrollable list box, something not possible with HTML alone. Applets are simply Java class files containing bytecodes that will be interpreted by the Web browser's built-in Java interpreter.

The applet inserts its user interface parts into the Web browser's window at the point in the HTML where the <APPLET> tag was encountered. The tag includes parameters for specifying how the HTML text will flow around the applet, and how much of the Web browser window the applet should occupy. There is also syntax for specifying hard-coded parameters to be passed into the applet.

When the Web browser sees the <APPLET> tag inside an HTML page, it goes back to the HTTP server where the HTML page originated to find the referenced Java class. Once found and downloaded, the Java class is interpreted, or run, by the Web browser. If you have ever seen a "Java applet started" message when browsing a Web site, you have encountered a Java applet.

Java applets have the full power of the Java programming language at their disposal, except for some restrictions regarding access to the local client workstation's resources for security reasons. That is, an applet cannot read or write files on the local file system or call any local file system executables. It can do remote system access via TCP/IP or HTTP, but only back to the server it came from (was served from). These restrictions are known as the sandbox that the Java applet must live within, which is a constraint not faced by a full-fledged Java application.

Note

You can write applets that do not have the sandbox restrictions by using a special process called digital signatures. These allow you to sign your applet so users can be guaranteed it came from you untampered. If the users trust you, they can allow signed applets from you to access their workstation resources. This works well in intranet environments, where you can predefine who is allowed to distribute signed applets for company-wide use, and you can identify which company staff are allowed to run them. (The flip side of signing is the ability to preauthorize only selected users to run the applet.)


Java Applications

The prospect of being able to embed Java applets inside HTML Web pages is certainly an enticing one. That is the fuel that has fed Java's wildfire acceptance. However, you can write full-fledged applications with Java, as well. These might be server-side applications that run on the server and do business logic or data manipulation, or client-side applications that display full-fledged user interfaces, access the server for data, or call programs on the server. These applications are invoked by running the java interpreter command on them. You pass to the interpreter command the name of the Java class file to be interpreted.

Java applications are portable to Java-enabled operating systems (and all operating systems are Java-enabled by now). Java applications have the advantage of having their own main window and user-interface real estate. They also do not have the sandbox restrictions that were described for Java applets. However, if they are client-side GUI applications communicating with a server-side program, you have the responsibility of distributing that application to all users' machines.

For servers such as OS/390 and OS/400, you can only run Java applications, not Java ap-plets. While there is technology for running GUI Java applications on both systems (see appendices D and E), it is recommended that your server-side Java applications be non-interactive. The role of these applications will be the usual business logic, without the user interface part. For the client part of your application, you need to make a choice among raw HTML, HTML with applets, or full-function applications.

This book focuses on the technology for writing Java code that can be used inside applications, applets, or Servlets.


Javascript

On the client side, applications and applets are frequently used in intranet environments where there is a well-defined user population and a high-speed network. For Internet environments or far-flung intranet environments, we see a return to raw HTML. This is because HTML has the fewest client requirements (any Web browser can run it) and is by far the fastest option when slow modem connections have to be supported. As we discussed earlier, you use Java Servlets running on your Web server to dynamically create the HTML Web pages and process the input from them. Since the Servlet is running on your server, it has access to your data, applications, and transactions there. It will draw on these resources to process the input and build up the content of the pages to be displayed. For example, a customer might enter his bank account number in an HTML form, and the Servlet would use that to access his account information and generate an HTML page to display that information.

The original downsides of HTML as a user-interface language were that it was weak in GUI constructs and not programmable. Hence the initial excitement in Java applets, which allow you, for example, to display an entry field that knows it is only allowed integer numbers as input. With raw HTML, you have to use a form and send the data to the server first, where it is verified. If the user types something incorrectly, the user has to wait for an entire new page to be returned from the server in order to see the error message. A Java applet can catch syntactical errors immediately and display the error message immediately, without a trip to the server.

Time has moved on for HTML since the early applet days. There is now an industry-standard version of HTML called dynamic HTML (DHTML). DHTML is a combination of HTML, plus something new called cascading style sheets, and a new embedded language called JavaScript. Cascading style sheets offer much improved visual control over your Web page, and allow you to set corporate look-and-feel standards across your entire Web site. JavaScript offers an alternative to Java applets for writing code to run directly in the Web browser.

JavaScript looks like a subset of the Java language, but it is quite different. It is embedded via <SCRIPT> tags directly into the HTML and is interpreted directly by the Web browser. It is not compiled into bytecodes like Java applets.

Because it does not require another trip to the server, JavaScript tends to be much more efficient than Java applets. JavaScript works with a document object model (DOM), which is an exposure of the entire Web page contents in a programmatic API that JavaScript code can access. JavaScript can be used to create functions, which are then assigned to specific events in your Web page, such as a user pressing a button. The JavaScript function can then use the DOM APIs to query and set the contents and state of the Web page, such as entry-field contents. Thus, simple syntax error-checking and error-message displaying is easily done directly in the Web browser without the need for Java applets. However, JavaScript has no communications or GUI capability, so sometimes it is augmented with a few Java applets to improve the user interface or to communicate with a server.

We do not cover DHTML or JavaScript in this book, but there are many good books available on these subjects. Also, they are an evolving industry standard being defined by the W3 consortium that oversees Web standards. You can read about them at the W3 Web site, www.w3.org.


Javaserver Pages

DHTML pages are usually created on the fly and served up by a Java Servlet running on the server, as described earlier. Also gaining in popularity are JavaServer Pages, or JSPs. These are HTML or DHTML source files that have embedded actual Java code. When an HTTP server first calls a JSP file, it will compile it on first touch to a Java Servlet that writes out the HTML source.

Typically, a hand-authored Servlet gets control from an HTML form, processes the request by collecting the data needed for the output page, and then "calls" a JSP to display the output. The output is passed in the form of a Java bean. There is an API for this call. The JSP contains all the static HTML, plus embedded Java code to extract the data from the Java bean and insert it into the static HTML, forming the final page that is sent to the user. The important thing to note is that the embedded Java code in the JSP is preprocessed on the server-side by the HTTP server, while any embedded JavaScript tags are sent to the client and processed there by the Web browser. This gives the ultimate in flexibility, permitting very dynamic content and behavior.

The advantage of JavaServer Pages over simply writing out the HTML from a Servlet is that it allows separation of the user interface source (the HTML) from the business logic source (the Servlet). This means one skill can be used to author the HTML by using a WYSIWYG (what you see is what you get) tool, and another skill can be used to author the business logic needed to get the data to be displayed in the HTML Web page. Often, the former requires a bit of the skills of an artist, and the latter, a bit of a programmer. You can see some JavaServer Pages in chapter 14.


Enterprise Java Beans

Server-side Java beans are "growing up" too. They are evolving into enterprise Java beans (EJBs), which support such enterprise-scale ideas as transactions, persistence (storing their variable data in a database), and Internet lookup by name (a naming and directory service). EJBs are arguably the future of server-side Java. Their goal is to provide a standard environment across all operating systems and to enable enterprise-scale Java beans to be built or bought and easily incorporated into any application. That is, they want to be the component model for line-of-business applications. To this end, all EJBs live in a standard server, or shell, which hosts them and supplies all the ugly "plumbing" that big applications need.

EJB authors focus on business logic, not plumbing. EJBs and their shell form a standard produced by Sun Microsystems with tremendous input from many companies, including IBM. Many vendors supply standard-conformant shells, and the holy grail is that any EJB can be deployed in any vendor's shell. The current reality is that this is a very young technology, although it continues to mature. Therefore, EJBs hold tremendous promise. For more on EJBs, see chapter 14.


Java Versus COBOL

You will see, if you have not already, that we are positive on Java in this book. We don't mean to imply by this that COBOL is not also a great language. It certainly is. But we are not here to sell you on COBOL or teach you COBOL. We are here to sell you on Java and teach you Java. We do apologize if you find the tone too evangelical; it was not our intention. However, at a reviewer's suggestion, let us highlight some of the areas in which we find Java has the advantage over COBOL:

  • Portability.

  • Syntax (arguably).

  • String support (arguably).

  • Date and time support.

  • Internationalization support.

  • Data structures such as arrays, vectors, hashtables, and collections.

  • Graphical user interface support.

  • Object orientation support.

  • Thread support.

  • Communications support.

  • User-defined functions (called methods in Java).

Now, to be fair, here are some of the areas in which COBOL has the advantage over Java:

  • Performance, especially database throughput.

  • Database access support.

  • Batch update support.

  • File sorting support.

  • Access to other languages.

As you read this book, make your own decisions. However, it does not have to be an either-or discussion. All languages have their strengths and weaknesses, and clearly there is room for multiple languages in our discipline. With Java, you have one more option.


Summary

HTML, DHTML, applets, Servlets, JavaServer Pages and enterprise Java beans are interesting, exciting, fun, and popular. They are all in your future! IBM's strategic product offering to enable these is called the WebSphere Application Server. This product is available on OS/390 and OS/400. The core functions of Web page serving-Servlets and JavaServer Pages-are freely built into these systems in their HTTP Server software. WebSphere Application Server can use anybody's tools, but IBMs' is WebSphere Studio, which includes HTML and JSP editors, and VisualAge for Java, for development of Java Servlets, applets, applications, beans, and enterprise Java beans.

We encourage you to explore all these topics, perhaps by starting at the one-stop-shopping Web site www.ibm.com/java. However, they all will require you to know the basics of the Java programming language, which is the focus of this book. It will put you well on your way toward absorbing these new topics and many more in the very rapidly expanding world of Java.

Here is a summary of the topics covered in this chapter:

  • Beyond all the hype Java is, after all, a programming language.

  • Java is an interpreted language (although traditional compilers for Java now exist).

  • Java is free, and is shipped as the Java Development Kit (JDK).

  • Java source code is compiled via javac into Java bytecode.

  • Java applications are invoked via the java command.

  • Java applets require the HTML <APPLET> tag.

  • Java Servlets are invoked by the HTTP Server software to prepare or respond to a Web page.

  • JavaServer Pages are HTML files with embedded Java code, preprocessed on the server.

  • JavaScript is embedded directly into HTML and interpreted by the Web browser.

  • Enterprise Java beans are an evolving industry standard for server-side components.

  • IBM's Java development tool is VisualAge for Java.

  • IBM's JavaServer Page, Servlet, and enterprise Java bean runtime is WebSphere Application Server.

  • IBM's WebSphere Application Server development tool is WebSphere Studio.

  • Both COBOL and Java are great languages, and each has its own strengths and weaknesses.

There are many exciting technologies to explore after learning Java, and you are no doubt already anxious to get to them! First, though, we suggest you relax and take it one step at a time


References

The following Web sites provide more information on the concepts and technologies dis-cussed in this chapter:

W3 Consortium home page: http://www.w3.org.

HTML online reference: http://www.w3.org/TR/REC-html40/.

InterNIC home page: http://www.internic.net.

Perl home page: http://www.perl.com.

JavaSoft Web page for Windows JDK (look for JDK or SDK): http://www.javasoft.com or http://www.java.sun.com/products.

Java for OS/390 Web page: http://www.ibm.com/s390/java.

Java for OS/400 Web page: http://www.ibm.com/as400/java.

IBM WebSphere Web page: http://www.ibm.com/websphere.

Overall IBM Java Web page: http://www.ibm.com/java.


Chapter 2: Java Flow of Control

Overview

This chapter discusses the outside skin of a Java application. To facilitate your under-standing of this discussion, you might first want to read appendix A or appendix B. These appendices review the flow of control for applications written in COBOL for OS/390 and OS/400. In essence, we will peel the onion, looking at Java from the outside in. (No crying, now!) This is an extensive chapter that covers a good deal of ground, but when you are done with it, you will be well on your way to being familiar with the overall architecture of Java applications.

Note

You might feel like you are "drinking from a fire hose" in this chapter as many new concepts are thrown at you. Do not despair: subsequent chapters contain more information and examples that make these concepts clearer.

You'll eventually get it. Grab a "byte" to eat if you need to, then begin.


Java's Outer Skin

In Java, your application is comprised simply of a number of class files (which have the .class extension). Class files contain Java bytecodes, which are compiled from Java source. Recall that bytecodes are not machine code that a traditional compiler produces, but simply a more efficient way to represent your source.

It would be too slow to interpret source directly. Bytecodes are in binary format, so they are easier to parse. They remain, however, completely platform-independent.

A typical Java application has a primary class file that gets invoked from the command line, and many additional class files that are used by this first class file, as shown in the Figure 2.1.

Figure 2.1: A primary class file usingmany additional class files.


Compiling

Let's review how you compile COBOL source, and then compare that to compiling Java source.

Compiling COBOL

In OS/390 LE and OS/400 ILE COBOL, you compile source code into an intermediate object or module using a COBOL compiler. These intermediate objects are then bound together into a single executable object, and the intermediate objects are disposed of. This binding step is called linkage editing or binding, as shown in Figure 2.2.

Click To expand
Figure 2.2: Compiling, binding, and linking COBOL.

The object-program-to-object-program calls between the modules within the executable object are done using static CALL statements. The bind step resolves the addresses of the bound objects once, and these addresses are used at runtime. The executable-to-executable calls are done using either static or dynamic CALL statements. Programmers today typically prefer dynamic calls because they allow for better reuse in that all reusing executables don't grow in size, and better maintenance in that dynamic replacements are easier. The address of dynamically called executables is determined at runtime, on the first call.

Compiling Java

As shown in Figure 2.3, you compile Java source code (with the extension .java) into bytecode (with the extension .class) using the javac compiler command.

Click To expand
Figure 2.3: Compiling Java source with the javac com-piler

There is no bind step, but compile-time checks are made on the referenced classes to ensure that they do exist and have the referenced entry points (unlike COBOL). You will see the syntax for calling between Java classes soon, but for now, just remember that all such calls are dynamic in Java.

At runtime, the first call to another class will cause Java to search for it on disk, verify that its contents haven't been tampered with and are valid, and then load it into memory. The creators of Java made a decision in favor of easy reusability and maintenance, versus the first-call performance benefit of static calls.

On OS/390 and OS/400, you typically do not do the javac compilation on the server, although you certainly can. Rather, this step is usually performed on your workstation- on Windows, for example, using the Sun JDK or the built-in JDK in VisualAge for Java. The resulting .class file is then copied or transferred to the OS/390 HFS (Hierarchical File System) or OS/400 IFS (Integrated File System), where it will reside and from which it will be run. Accordingly, you do not typically use ISPF or SEU (Source Entry Utility) to create and edit your Java source, but rather workstation-based tools like VisualAge for Java. (More details on this are available in appendix D for OS/390 and appendix E for OS/400.)

Note that OS/390 also has a traditional-style "static" compiler for Java, as well as a Java interpreter. The static compiler on OS/390 does, in fact, allow you to bind compiled Java modules together into executables and DLLs, just as COBOL does. OS/400 uses a slightly different technology for its "direct execution" compiler, so it does not have an explicit bind step, but it still offers tremendous performance gains over the interpreter. So, you do have some flexibility in your choice of server-side Java.


Flow of Control

Now let's look at how control flows within an executing application, first in COBOL (for comparison's sake), then in Java.

COBOL Flow of Control

With COBOL, the main module of the first executable object (executable load module for OS/390 or *PGM for OS/400) gets control from the command line in the first paragraph of the PROCEDURE DIVISION. From this ini- tial code on, control is passed to other paragraphs in the PROCEDURE DIVISION, to nested programs inside this module, to programs in other modules statically bound to this executable object, and dy-namically to other executable objects. Figure 2.4 shows what a typical module (a compiled COBOL source program) looks like.

Click To expand
Figure 2.4: A typical compiled COBOL source pro-gram.

COBOL and Functions

COBOL source programs have para- graphs for modularizing routines inside that one program. These are not quite like functions in other languages be- cause they cannot take parameters, have local data items declared inside them, or return values. Because they do not re- turn values, they can only be called by standalone PERFORM statements, versus being called from within expressions, as intrinsic functions can be. Further, they cannot be called directly by other COBOL programs.

Some relief has arrived in later COBOL releases in the form of nested programs. With these, you can now insert an entire COBOL source program inside another one, just prior to the END PROGRAM statement that must end the parent pro-paragraphs themselves that can be gram. You can nest programs inside other nested invoked, and hence are interesting programs, as well. These nested programs pro-vide the benefits of being able to pass parameters via a CALL...USING statement, return values using the RETURNING clause, and declaring WORKING-STORAGE data items only visible to the nested program. They do not offer the ability to be externally called by outside programs, though; only the containing parent program can call them, or sibling nested programs if the COMMON attribute is specified. Also, these nested programs can only be called using the CALL statement, and not as part of an expression. Still, nested programs are an important new tool to help modularize your code into more self contained and "encapsulated" functions.

Note

Of course, paragraphs can be contained inside sections, but it is the paragraphs themselves that can be invoked, and hence are interesting to us.

However, COBOL still does not have the ability to create the equivalent of your own intrinsic functions. Intrinsic functions are COBOL-supplied functions (as defined in ANSI 85) that can be called within an expression wherever a literal or data item is expected. Their returned value is substituted into the expression at runtime. They take parameters, and are categorized according to the type of the returned value. The following is an example of the alphanumeric function UPPER-CASE, which takes a string and returns a capitalized version of it:

MOVE FUNCTION UPPER-CASE("abc company") TO COMPANY-NAME-UC.

Note the keyword FUNCTION, which tells the COBOL compiler that you are about to call a function.

Here is an example of a numeric function being used in an expression:

COMPUTE PROJECTED-SALES-2Q = FUNCTION MEAN (SALES-JAN,
SALES-FEB, SALES-MAR) * 1.5.

As you will see, unlike COBOL, Java does have the ability to create your own functions.

Data Items in COBOL

COBOL source programs contain global data items in their WORKING-STORAGE SECTION. By this, we mean that all code in the PROCEDURE DIVISION and in nested programs (if you use the GLOBAL clause) has access to those data items. The other COBOL modules in this executable object have access to them, too, if you use the EXTERNAL clause. These data items hold their values between calls, unless you use the INITIAL clause on the PROGRAM-ID paragraph, or cancel the program between calls. In COBOL, however, you don't have the ability to declare data items locally inside a paragraph that are reset between calls.

These types of data items are called automatic because their memory is automatically allocated with each call, and released at the end of the call. Other languages that support the notion of user-defined functions do support these automatic data items (including Java, as you will see). The closest things in COBOL are the data items declared in nested programs, if you think of nested programs as being similar to functions, since they can take parameters and return values. The values of these are still persistent, but at least their persistence is tied to the persistence of their parent program.

Another analogy to automatic data items are LOCAL-STORAGE data items, which OS/390 COBOL supports but OS/400 COBOL does not (as of this writing). These are still global to the program rather than local to a paragraph, but their values are reset with each call to the program (that is, module). So, the use of LOCAL-STORAGE data items inside nested programs gets pretty close to the idea of functions in other languages, with respect to local variables, parameters, and return values. The only thing they are missing is the ability to be called externally by programs other than their parent or siblings. Keep this in mind in the following, as you learn about methods, global variables, and local variables. Yes, the term "variable" in Java is equivalent to "data item" in COBOL.

Java's Flow of Control

How does COBOL's flow of control compare to Java? Java has class files (dot-class files) that contain…what? Not programs. Rather, Java class files contain classes, which are Java language constructs. They are similar to COBOL group items (or data structures in other languages), in that there is explicit language syntax for defining them, and they contain variables defined inside them. As with COBOL group items, a class's memory requirements are determined by the cumulative memory requirements of its variables. Unlike group items, however, in Java you can also define functions right inside the classes.

Method is Java's term for a function. Methods take parameters, have their own local variables, and return values. They can be called by a standalone statement or from within expressions. In Java, you place all your executable code inside methods. There are no DIVISIONS in Java, and no COPY members. It is really blissfully simple: a class contains both variables, which are global to all the methods in that class (and potentially other classes), and methods, which contain parameters, local variables, and executable code.

That's it! Variables and methods. Easy. Thanks for your time, you are now a Java programmer! Well, not quite, we do have to describe a few details about these items…

Defining Java Classes

Figure 2.5 shows what a class (named Class1) looks like at a high level, inside the un-compiled source file (the dot-java file) and inside the compiled class file (the dot-class file). Figure 2.6 shows a high-level view of a method.

Click To expand
Figure 2.5: A high-level view of a class.

Figure 2.6: A high-level view of a method.

The variables inside a class can be defined before or after the methods. Some people advocate putting them after the methods because they are implementation details. Our feeling, however, is that it is more natural for programmers to see the variables first, so they have a context when they see them later in use. It is simply a style choice, though, and one that is entirely up to you.

The local variables inside a Java method can be declared anywhere, as long as they are declared prior to being referenced. Convention is that they go at the top of the method, although some people prefer to declare them just before they are first used.

A better analogy for a Java class than a COBOL group item is a COBOL program. Java classes contain methods and global variables that all methods in the class can access. COBOL programs contain paragraphs and global WORKING-STORAGE data items that all paragraphs can access. If you like, since paragraphs are not true functions while methods are, you might prefer the analogy of Java methods to COBOL nested programs.

We are ready, then, for a first rough analogy of COBOL parts to Java parts, in Table 2.1.

Table 2.1: Comparing COBOL and Java Anatomies.
COBOL | Java | Comments
Application (one or more run units) | Application | A collection of programs or classes used together at run time
COBOL source | .java file | What you type.
Module | class file | The compile result
COBOL program | Class | Inside a module
W-S data items | Variables | Accessible by all code in compile unit
Paragraphs or nested programs | Methods | Functions or subroutines
L-S data items in nested programs | Variables in methods | Automatic, localized variables
Code | Code | Executable code

Java Class Syntax

What does a Java class look like? The syntax looks like Figure 2.7.

Click To expand
Figure 2.7: The syntax of a Java class.

You declare the class in your .java source file using explicit syntax to start and end the class. It optionally starts with the keyword public (a modifier), followed by the required keyword class, followed by the name you want to give the class. Finally, there are curly braces { and } that start and end the body of the class. All variables and methods are defined between the braces, and the closing brace must always be the last non-blank line of the source file.

Note that the name of the class must match the name of the Java source file and the compiled Java class file-exactly, including case. You can define multiple classes per source file, as long as only one is public, but each becomes a separate class file after compile. However, it is considered bad form to define multiple classes per source file, except when defining inner classes (which we don't discuss until chapter 14).

The public modifier in Figure 2.7 states that this class is freely accessible by all other classes that choose to use it. The default without using this optional modifier means that it is only accessible by other classes in this package, a construct you will learn more about later in this chapter.

The syntax for defining a Java class, then, is as follows:

<modifiers> class Name
{
...
}

Please note the following:

  • The angle brackets around modifiers indicate that they are optional. You have seen the public modifier, but there are two more (abstract and final) that you'll learn about in chapter 9. There can also be more than one modifier, separated by blanks, as in public final.

  • The keyword class is not optional. It specifies the type of Java construct you are defining. The class name is case-sensitive and totally up to you. By convention, the first character of the class name is in uppercase, as is the first character of each word, as in AccountsReceivable. Names cannot have blanks, but practically every other character is allowed. However, because the file name must match the name of the class, the file system keeps you from using some characters, like the backslash and forward slash.

  • The braces { and } define the beginning and end of the class block. You will see these used throughout Java for defining all blocks or "scopes," as with C and C++.

All Java source is free-format. The compiler ignores redundant white spaces and all line breaks. In fact, you could put every word on its own line if you wanted!

Java Variable Syntax

Inside the braces of a Java class are the variables and the methods of the class. Variables are declared using this syntax:

<modifiers> type name;

The optional modifiers include public, which is like the EXTERNAL clause in COBOL. The type is one of eight predefined data types ("categories" in COBOL terminology) or another class name, and the name is again whatever you want to use. The convention for variable names is the same as for method names (which is coming up). Note the ending semicolon. All statements in Java must end with a semicolon, much like the period in COBOL.

You will see much more about Java variables as you progress, but for now, here is an example:

public int commissionRate;

This variable is accessible by other classes (public), is of type integer (int), and is named commissionRate.

Java Method Syntax

The syntax for defining a Java method inside a class looks like this:

<modifiers> return-type name(<parameter-type parameter-name ...>)
{
     ...
     return return-variable;
}

Please note the following:

  • The list of modifiers is larger than for a class. (These will be covered later.) As with classes, you need to use the public modifier to allow any other arbitrary code access to this method.

  • The return type is the type (category) of variable this method will return, such as int for integer. It is not the name of the variable returned, as COBOL's RETURNING clause expects. If the method does not return a value, you must specify the keyword void.

  • By convention, method names are all lowercase, except the first letter of any words other than the first, as in getName and getBillRate. The other conventions are the same as for variable names. Names in Java are very case-sensitive, and they have no limit on length.

  • The parameter list is a type-name pairing. Each pair is comma separated. If the method does not take parameters, just code empty parentheses.

  • The return statement is needed only if you return a parameter; otherwise, return with no parameter is acceptable, as is no return. The type of the variable you return must match the return-type in the method declaration.

There are two predominant styles for Java braces, the wrap-around brace style:

public void myMethod() {
   ...
}

and the line-up brace style:

public void myMethod()
{
   ...
}

We prefer the latter, because this alternative makes it easier to line up the braces in an editor to ensure that you have not missed one. There is no official consensus, however; it is a personal choice.

Using Java Classes

Classes in action are called objects. With objects, we firmly leave behind the world of COBOL and other 3GL languages. After learning about objects, you will see how program flow happens in Java via the inter-operation of objects.

When you define a class, you are merely defining what the class will look like to the compiler. You have not defined something directly usable by other programmers. A class is merely a template; it does not reserve any memory or storage. In order to use a class, programmers must declare an instance of it, just as you always have declared instances of numeric or alphanumeric data: by declaring a data item of that type. In effect, a class defines a new category to the compiler. Indeed "class" is short for "classification."

For example, assume that you have defined a new class called MyClass. To subsequently use this class, you must define a variable whose type is that class name (versus, say, one of the eight built-in data types such as int):

MyClass myclass1;

This statement defines a variable to the compiler, of type MyClass and name myclass1. Note the ending semicolon, with which all statements in Java end. Note, also, that you can define more than variable with the class type, like this:

MyClass myclass1;
MyClass myclass2;

Declaring a variable by specifying the class name as the type, as shown here, is the first step, but it is not the only step. The variable must also be equated to an instance of MyClass-an object, in fact.

An object is a particular instance of a class. It is a piece of memory that is big enough to hold all the cumulative memory requirements of all the variables in the class. So, to create an object, you need a way to allocate this memory. This is done using the new operator, and telling it the name of the class. The result is a new piece of memory big enough to hold all the variables inside that class. So, you use new to allocate an object, or particular instance of the class. This can either be done at the time the variable is declared, or after, for example:

MyClass myclass1 = new MyClass();
MyClass myclass2;
myclass2 = new MyClass();
Syntax Note:

The equals sign (=) is used in Java to equate a value to a variable, as it is in the COMPUTE statement in COBOL. Note that it is also used to initialize variables in Java, versus the VALUE clause in COBOL.

This assigns your variable to a piece of memory that is allocated by the new operator. Please note the parentheses () after the name of the class. This is the required syntax. This allocation is known as object instantiation. Your variable (say, myclass1) is given the address of this allocated memory. Thus, object variables do not contain objects per se; they contain addresses of objects. Java calls these addresses references.

Each object gets a unique copy of the variables declared in the class. Each object can change its copy of the variables without affecting the variables in other objects.

It is very important to think of object variables, such as myclass1 and myclass2 in the preceding example, as references to objects, versus actual objects. This is a key Java con- cept. Unlike regular data type variables (such as int for integer), object variables require the second step of pointing them to an object. An object reference variable is the official term for any variable whose type is a class name, versus one of the built-in data types.

In our example, myclass1 is an object reference variable. It contains the memory address or reference of an allocated object. Each such variable is allocated four bytes by the com- piler, enough to hold a memory address.

This concept of an object reference variable holding a reference to an object carries the implication that you can change on the fly which object the variable points to:

MyClass myclass1 = new MyClass();
MyClass myclass2 = new MyClass();

Now, myclass1 and myclass2 each point to separate objects in memory, as shown in Figure 2.8.

Click To expand
Figure 2.8: Object reference variables pointing to separate objects.

Consider this new line:

myclass2 = myclass1;

This line is interesting because it resets myclass2 to refer to the same object in memory that myclass1 does, as shown in Figure 2.9.

Click To expand
Figure 2.9: Multiple object reference vari-ables pointing to the same object in memory.

Changes made to variables using myclass1 will now affect what you see using myclass2. So what happens to the second object in memory? If no other references exist to it, it will be vacuumed up by the garbage collector. This always runs in the background, looking for dead objects like this toreclaim.

Note that you can only assign an object reference variable to an instance of the same class to which the object reference variable is declared a type. For example, the following is usually not valid because the declared type (MyClass) does not match the allocated type (YourClass):

MyClass variable1 = new YourClass();

We say "usually not valid" because there are exceptions, related to class inheritance, which you will see in chapter 9.

In summary: You use a class by declaring a variable and specifying the class name as the type, and then allocating an instance of the class with the new operator. This allocates unique memory for each of the global variables in the class. A particular instance is called an object. You can have multiple objects of the same class, but each has its own copy of the variables that are defined inside that class. A class is like a template that describes common information for all instances of that class:

  • What the variable types and names are in each instance (and any initial values you specify).

  • What methods, and their code, are available in each instance.

We discussed how a Java class is somewhat analogous to a compiled COBOL program, but really it is more accurate to equate a COBOL program to a single instance of a Java class because in a COBOL application, you can only ever have a single set of values for all the WORKING-STORAGE data items. Those values may change over time, but there remains only one place in memory for them. Contrast that to Java, where the memory for the variables in a class are not allocated implicitly by the runtime when the application starts, but rather are allocated explicitly by you when you use the new operator, and you can use that operator as many times as required per class.

For anyone learning Java or other object-oriented languages, the concept of a class as being merely a template of something that must be instantiated in order to be used causes the greatest confusion initially. The "how" is pretty easy-just remember to use the new operator-but the "why" gets people scratching their heads. This will come with time. For now, however, imagine a class that represents information about an employee, a purchase order, or a customer. The ability to have a bunch of these active at once, each with unique data per unique employee, order, or customer, is the goal. This is much like multiple occurrences of group items-multiple copies of data each sharing the same format.

However, not only do objects share a common format for the data, they share the same methods! You can call the same method on two objects of the same class, and the only difference between them will be the value of the data used in those methods. By clumping the data with the methods, you get very nice encapsulation, which leads subsequently to very easy reuse in future applications. The code plus its dependent data items do not have to be ripped out of a larger code base to be reused in a different situation.

A popular analogy of classes and objects is that of a class being a cookie cutter and the objects being the actual cookies. These concepts, and why they are a good thing, will become clear as you read the book. You will be leaving a trail of cookie crumbs in no time. That is not to say you will be writing crumby code, though!

Accessing Members in a Java Class

With an object reference variable, you can now access the variables and the methods inside that instance of the class. Collectively, variables and methods of a class are referred to as class members. Suppose you already have a class called Stack that manages integers on a last-in, first-out stack. It might have methods push and pop that put an integer on the top of the stack and remove the top item from the stack, respectively. Here is what the code in another class, which uses the Stack class, might look like:

Stack myList = new Stack(); // allocate instance of stack class
myList.push(100);           // stack contents: 100
myList.push(200);           // stack contents: 100, 200
int topValue;               // declare an integer variable
topValue = myList.pop();    // topValue: 200, stack contents: 100
Syntax Notes:

  • Line comments in Java start with a double forward-slash (//). All text from the double slashes until the end of that line is considered a comment.

  • Java has "primitive" data types (categories), such as int for integer, as well as object reference variables. All eight primitive data types are discussed in detail in chapter 5.

The Stack class is a good example of the power of objects because it is likely that your program will use more than one stack. Each one is another instance of Stack; that is, each one is a unique object.

If you were writing a COBOL program that used stacks, you would be limited to only one stack at a time because there would only be one chunk of memory for the table and its index data item in any one run unit. One way around this would be to have a separate table for each stack and a separate index data item for each stack. Adding another stack would require carefully creating a new table and index data item, using unique names. The code using the stacks would have to be careful to reference the correct names. In Java, you simply instantiate another Stack object.

Once you have an object in Java (in this case, myList) you can call the methods inside that object by using the dot operator on the object. The dot (a period) comes after the name of the object, and the method name comes after the dot. Then, you place the parameters inside parentheses, versus COBOL, where you would use the USING phrase on the CALL statement. This ability to directly call methods inside a Java class is a big distinction from COBOL programs, where you cannot call paragraphs or nested programs directly from other COBOL programs, but rather have to call the program as a whole. Another distinction, already mentioned, is the ability to have multiple instances of the class, and hence its variables and methods, active at the same time.

You can access variables inside a Java object in exactly the same way as you access or call methods, using the dot operator. Suppose the Stack class has an integer variable inside it called topIndex. This variable holds the table index of the top item in the stack. You could change that variable directly, as long as you qualify the variable reference with the object name, for example:

myList.topIndex = 12; // change value of topIndex in object myList

Since multiple objects each have their own copy of the members in a class, it is very necessary to tell the compiler which version of the members you want to use by identifying the object. Note that you use the name of the object reference variable, not the class name, to prefix or qualify the method or variable name.

Variables defined at the class level have unique storage per object or class instance, so they are usually referred to as instance variables. Their value depends on the instance of the class in which the reference is made to them. "Instance variable" is a better term than "global variable," and we will use this term from now on.

Since every variable and method in Java must exist inside a class, it follows that your code will be littered with xxx.yyy and xxx.yyy() expressions. That's life in an object-oriented world: You need to be "qualified" to do it! Hopefully, you understand why the qualification is necessary. The compiler and runtime need to know not only which class the method or variable is in (because two classes can and often do define methods and variables with the same name), but also which instance of the class you actually are referring to.

Not every member (variable or method) reference needs to be qualified with an object name. The exception occurs when you are referring to members in the current class in which you are writing code. If your code refers to a variable without qualifying that variable with an object name, the Java compiler looks first for that variable definition in the current method (local variable or parameter name). If it is not defined there, the compiler looks for the variable definition in the current class in which the method is defined (instance variable). If it is not found there, you get an error. Similarly, if you refer to a method without qualification, Java looks for that method in the same class, and gives an error if not found there.

Referring to a variable without qualification (either to reference or modify it) is equivalent to telling the compiler that you are referring to a variable in this class-in this class instance, actually. In fact, there is a special predefined object variable in Java called this. Sometimes you might need to use it. For example, if you define a local variable of the same name as an instance variable, and want to reference or use the instance variable, you have to qualify it with this, as shown in Listing 2.1.

Listing 2.1: Using this to qualify an instance variable.
Start example
public class Aclass
{
    int george;  // integer instance variable
    int phil;    // another instance variable

    public void aMethod() // start of method aMethod
    {
       int george;        // local variable
       george = 1;        // change the local variable
       this.george = 2; // change the instance variable
       phil = 3;          // No local "phil" so not ambiquous
    } // end of method aMethod
} // end class Aclass
End example

In the example shown in Listing 2.1, there is a class called Aclass, that contains two class instance variables, george and phil, each of type integer. This class also contains a method called aMethod, which contains a local variable called george, and some Java logic to assign a value to all three variables-the two instance variables and the one local variable. Since the variable george is declared both locally in the method and globally at the class level, the local variable hides the class instance variable. The only way to access the instance variable is to qualify it with this to tell the compiler you are referring to the variable declared in this class instance.

This redefinition of variables in both the global and local scope is called data hiding, and is not good form because it can lead to interesting bugs. It's better to always use unique names, perhaps by following some naming convention for global versus local names. What programmers often do, however, is use the same name for both an instance variable and a parameter to a method. You quite often will want to restrict other classes from directly accessing an instance variable by using the modifier private. If users can't reference or modify the variable, you have to give them methods to do that instead. For example, you might supply a method named setXXX that takes a parameter that is used to set the instance variable value. (Similarly, you might supply a method named getXXX to retrieve the instance variable value.) Note that XXX is usually the same text as the variable name. In these cases, the parameter name used in the method is often the same name as the instance variable, to indicate their affinity. Since this hides the instance variable from the code within the method, you have to use the "this dot" qualification, as shown in Listing 2.2.

Listing 2.2: Using this dot qualification.
Start example
public class ItemOrder
{
    private int item;     // id of item
    private int quantity; // number of items in order

    // A method for placing an order
    public void placeOrder(int item, int quantity)
    {
        this.item = item;         // assign parameter to instance variable
        this.quantity = quantity; // assign parameter to instance variable
    }
}
End example

In Listing 2.2, a method called placeOrder takes two integer value parameters, named item and quantity. The values passed as parameters are used to set the instance variables, which have the same name as the parameters. To distinguish the instance variables from the parameter variables, the instance variable references are qualified with this.

This concludes our detour examining objects in Java. You are now "qualified" to continue!

Running a Java Program

You now know how to define classes, declare and allocate instances of them (as object reference variables), and invoke methods and change variables in them. In other words, you know how to use objects. It's time to review what Java flow of control looks like in a typical Java application, as shown in Figure 2.10.

Click To expand
Figure 2.10: Java flow in a typical application.

The main method (covered later) of the initial class gets control first. It instantiates instances of other classes (objects) and invokes methods on those objects as necessary. Although we would like to compare a COBOL program to a Java class and a COBOL paragraph to a Java method, method calls can't be mapped to paragraph PERFORMS, since one COBOL program can't directly call a paragraph inside another COBOL program. So, you'll have to use your imagination! Assume paragraphs, or better yet nested programs, could be called directly from other COBOL programs, and you'll help make the analogy work. (Thanks.) All this method calling is done in order to accomplish the desired task of the Java application, and when that has been done, the main method finally returns to the caller, and the application ends.

Calling all COBOL Programs

In COBOL, you call another COBOL program using the CALL statement:

  • To pass parameters, you specify the USING phrase. The called program's PROCEDURE DIVISION header must also specify a USING phrase. You can pass multiple parameters (blank delimited) or group the parameters inside a single group item. The data items to be passed by the caller are defined in the WORKING-STORAGE SECTION of the caller and the LINKAGE SECTION of the callee. The number of data items and the attributes of each must be the same in both the caller and callee, while the actual names used can be, and often are, different. The matching of parameters is by position, not by name.

  • To get the returned value, you specify the RETURNING or GIVING phrase, both on the CALL statement of the caller and on the PROCEDURE DIVISION header of the callee.

  • The CALL statement is a standalone statement. It cannot be used inside an expression, such as inside a COMPUTE statement.

  • Note that OO COBOL on OS/390 also has an INVOKE statement for invoking OO COBOL methods. However, we don't discuss OO COBOL here. It is covered in appendix C.

Calling All Java Methods

In Java, you don't call other classes per se; rather, you call methods inside other classes:

  • The method name is always followed by parentheses. Blanks around the parentheses are optional and ignored. To pass parameters, you specify one or more variable names inside the parentheses, separated by commas. Blanks around the variable names are optional and ignored. The number of parameters passed by the caller and defined by the called method must match, as must their data types. The names need not match and usually do not, as with COBOL.

  • To get a returned value, the caller equates the result of the method call to a variable. The callee must define the return type to be something other than void (either one of the eight primitive types such as int, or a class name), and must use a return statement to exit the method, specifying the value, variable, or expression to be returned.

  • Methods in Java can be called standalone or within expressions. In the latter case, the returned value of the called method is simply substituted into the expression, as with COBOL intrinsic function calls.

Here are some things to keep in mind about invocation options in Java:

  • Java method names can be any length and are very case-sensitive.

  • Methods cannot be defined as taking optional parameters in Java.

  • Method names need not be unique within a class, as long as the signature is unique. The signature is the combination of the method name plus the number and type of parameters.

  • If the method does not take any parameters, empty parentheses must be specified on the call, unlike COBOL where you simply omit the USING phrase.

Table 2.2 compares the three invocation options in both languages.

Table 2.2: Comparing COBOL Program Calls to Java Method Calls.
COBOL | Java
CALL "PGM-B" USING PARM1 PARM2 RETURNING RESULT-VALUE. | resultValue = myObject.myMethod(parm1,parm2);
IF RESULT-VALUE IS EQUAL TO 10 ... | if (myObject.myMethod(parm1,parm2) == 10
CALL "PGM-B" USING PARM1 PARM2. | myObject.myMethod(parm1,parm2);

Java Overload! Java Overload!

Java allows you to have multiple methods with the same name, in the same class, as long as the signatures (number or types of parameters) are different for each. This is a key concept in object-oriented languages like Java, and it is called method overloading. You are effectively overloading the name of the method with multiple definitions. This allows you to imply to users of your class that in each case the operation to be performed is the same-only the inputs are different. You might, for example, have a String class that has two methods for substring. One method might take a single position number and return the substring from that position until the end. The other method might take a position number and a length number, and return the substring from the given position for the given length. This makes using the substring method more intuitive than having two methods named, for example, substring and substringWithLength.

Note that under the covers, the single parameter version of substring will likely just invoke the multiple parameter version with the remaining length of the string supplied as the second parameter. Methods of the same name can be made unique by differing numbers of parameters, or by differing types of parameters. So, you may have a method named max that returns the maximum of two given integers, and another also named max that returns the maximum of two given floating-point variables. The compiler and runtime can determine which one you mean to call by comparing the number and the type of the parameters on your call.

Method overloading is a particularly important feature of Java, since the language does not allow you to define methods that take a variable number of parameters (or optional parameters). COBOL CALL statements do allow for this, by specifying OMITTED on the USING phrase, offsetting somewhat the need for overloading in COBOL.

Prototyping Java Methods-Not!

Java, like COBOL, does not require or allow you to prototype your methods prior to de- fining them. Java does not even have the concept of COPY members, which is where these prototypes typically are placed. When you define a Java class and the methods inside it, you declare and define the methods in one step, as shown in the methods swap and com- pare in Listing 2.3. (By the way, the swap method shown here will not work. You'll dis- cover why shortly.)

Listing 2.3: Declare and define methods named swap and compare.
Start example
public class Testing
{
    public void swap(int parm1, int parm2)
    {
        int temp;
        temp = parm1;
        parm1 = parm2;
        parm2 = temp;
    }

    public int compare(int parm1, int parm2)
    {
        int temp;
        if (parm1 > parm2)
          temp = parm1;
        else
          temp = parm2;
        return temp;
    }
}
End example

If you do not have prototypes that can be included, how does the compiler verify the parameter and return types on calls to methods? It does this as part of its dynamic loading of classes: when a class is referenced or used in another class, both the compiler, and later the runtime, will verify that all method calls to an object of that class are correct. It does this by actually going out and getting the .class file object from the system as it needs it.

The COBOL compiler cannot and does not attempt to verify that you have got the parameters correct. Java, on the other hand, is able to do this because the Java compiler can actually find and load the referenced classes, and has the ability to query from a loaded class all the method names and for each method all the parameter information.

Defining Java Methods

As you have seen, in a Java method, you define optional modifiers, the return value (or void), the method name, and the parameters all in one statement (which can be on one line or multiple lines). Following this initial statement, the method body is enclosed in braces. It contains local variable declarations and logic, and ends with a return statement (if the method returns a value). The modifiers for methods, which are described throughout this book, are listed in Table 2.3.

Table 2.3: Java Method Modifiers.
Modifier | Description
public | Method is accessible by all.
private | Method is only accessible by this class.
protected | Method is only accessible by this class and those that extend it (see chapter 9).
static | Method can be used without instance of class.
abstract | Method must be overridden by classes that extend this one (see chapter 9).
final | Method cannot be overridden by classes that extend this one (see chapter 9).
native | Method is implemented in other language like C (see JNI in chapter 14).
synchronized | Only one thread is allowed to run this method at a time (see chapter 11).

Parameters passed to Java methods are always passed by value, compared to the default in COBOL, which is pass-by-reference unless you specify BY VALUE or BY CONTENT on the USING phrase. Pass-by-value means the contents of the parameters are copied at call time, while pass-by-reference means the parameter addresses are passed at call time. This means Java methods cannot alter the contents of passed parameters. There was an example in Listing 2.3 of a Java method named swap, which intended to swap the values of two given integers. This will fail in Java because any changes to the parameters do not effect the caller's values. For more on this, see chapter 6.


Code Reuse

Code reuse is an important goal of the programming world. By writing commonly needed functionality once, and reusing it in multiple places, you save time and improve quality. Let's review reuse in COBOL, then look at how Java enables reuse.

COBOL Code Reuse

Reusing common COBOL programs has been going on for some time. You achieve reuse in COBOL a number of ways, each with different levels of granularity:

  • Via common paragraphs that you copy into multiple programs.

  • Via common programs that you link-edit or bind into multiple executable objects.

  • Via common executable objects that you dynamically call in multiple applications.

  • Recently, via DLLs on OS/390 and service programs on OS/400 that you link to multiple executable objects, other DLLs, or other service programs.

The use of DLLs and service programs is still quite limited in the COBOL community, but it will grow with time. Because each of these can contain multiple compiled COBOL programs that can be called directly by the users of the DLL or service program, they give the most flexibility. (They are covered in appendices A and B, if you are interested in learning more.)

COBOL DLLs and service programs are not physically copied into the program object; they are simply "linked" to it. The same reusable object can be linked to more than one program object, as shown in Figure 2.11.

Click To expand
Figure 2.11: Linking the same reusable object to multiple program objects.

One way to look at DLLs and service programs, and even ILE and LE executable load modules, is that they allow you to group multiple reusable COBOL programs into a single unit. Presumably, all the compiled COBOL programs grouped together offer complementary functionality and are conceptually a unit. Rather than keeping each reusable COBOL program separate, it is often more convenient to package them together in some way that makes them more convenient to distribute and use, using DLLs or even executable load modules (service programs and program objects on OS/400).

Java Code Reuse through Packages

Java, like all object-oriented languages, is big on code reuse. Java classes are all about code reuse. As discussed earlier in this chapter, Java classes are like individual COBOL programs, but can be reused simply by instantiating them one or multiple times.

Does Java have any way to package multiple classes into a single unit, similar to CO-BOL's DLLs and executable objects? Yes. Java's mechanism for grouping related classes is called, appropriately enough, a package. For example, you might define a package called dataStruct, shown in Figure 2.12, containing classes for common data-structure algorithms such as stacks, linked lists, and queues.

Click To expand
Figure 2.12: Defining a package containing classes for common data-structure algorithms.

In Java, packages are a defined part of the language, as opposed to a file system object such as OS/390's load modules and DLLs or OS/400's *PGMs and *SRVPGMs. In the source file where a class is defined, you can optionally define the package of which the class is part, using the package statement.

You will almost always define all the classes you write to be part of a package in Java. This is, among other things, a convenient way to group your classes; soon enough, you will have dozens, hundreds, or even thousands of classes that will benefit from grouping.

Defining a Java Package

Inside the source file for a class, you define the package to which this class will belong by using the package Java statement, as shown in Figure 2.13.

Click To expand
Figure 2.13: Defining the package to which a class will belong.

Note the package dataStruct; statement at the top of all three source files. This is what creates a package. The package statement is essentially a compiler directive, informing the javac compiler that this class is to be considered part of the dataStruct package (in this example). All classes that specify the same name on the package statement are considered to be part of that package after compiling. This statement must, if specified, be the first statement in your Java source file.

If this statement does not produce some kind of file-system package object, then why bother using packages? There are three reasons:

  • Grouping. It is always helpful to partition numerous items into a smaller number of groups. This helps present a mental picture of the functionality at a glance.

  • Name qualification. When you use a class inside a package, you typically have to fully qualify the class name with the package name, as in dataStruct.Stack. This allows two classes to have the same name as long as their package names are unique.

  • Privacy. Classes in the same package can have accessibility rights to each other that outside classes do not have.

Since classes are designed to be small and concise, it is important to have a grouping mechanism.

Using Java Packages

The opposite of defining a Java package is using a Java package. If you have source code that needs to use the classes defined in a package, you have two choices:

  • Qualify all reference to the classes with the package name and a dot, as in

    dataStruct.Stack myList = new dataStruct.Stack();

  • Import the package using the Java import statement at the top of the source file, after any package statements, as in

    import dataStruct.*; // import all classes in dataStruct package

The import statement can also be used to import a single class from a package, as in

import dataStruct.Stack; // just import the Stack class

There is no performance implication to importing all classes versus just one class, so in most cases you will simply import them all.

When you import a package, you have unqualified access to all classes in that package. This frees you from the tedium of prefixing the package name to all class names used from the package. You can still fully qualify each class name with its package name, if you wish. If you do fully qualify each class name, then you do not need to use the import statement, since the Java compiler knows the package by the qualified names. Why would you want to do this? If you are importing two packages, and want to use a class that has the same name in both, you would have to refer to the class in one of them using full package name qualification.

If you do not specify a package statement in your class source file, then your class is considered to be part of the default package-that is, the "unnamed" package. There is exactly one of these, and all classes without package statements are placed there. Java always imports this unnamed package implicitly for you.

Note

Here is a tip that will save you some grief. If your class is in a package (there is a package statement), then you cannot access any classes in the unnamed package. (The reverse is allowed, however.) We are not sure why this rule exists, but it does, and it will probably bite you at some point.

Java Package Naming Conventions

The Java convention for package names is to use all lowercase letters, although it is not as strictly followed as the class, method, and variable naming conventions. It is important to choose package names that are as unique as possible in order to minimize the chance for collision with other package names. This becomes particularly important as Java packages become available on the market for global reuse and resale. To this end, the Java language allows packages to have multipart names, with each name separated by a dot. This allows for some clever naming conventions for all your packages, for example:

bobs.packages.dataStruct
bobs.packages.taxTables
bobs.packages.mathRoutines

Java has a standard convention for naming packages that ensures universal uniqueness. The general rule is to start all your package names with the reverse of your company's in-ternet domain name (minus the "www" part). For example, all packages supplied by IBM start with "com.ibm," and yours should start with "com.yourcompany":

com.yourcompany.dataStruct
com.yourcompany.taxTables
com.yourcompany.mathRoutines

As you will discover throughout this book, Java supplies all of its classes in packages, and these all start with java, as in java.lang, java.math, and java.util. Almost every Java class you look at will import one or more of these JDK-supplied packages. An all-important package, containing some critical core classes, is the java.lang package. Because of its importance, this package is always imported for you, just as the unnamed package is. The rest you have to explicitly import, if you need them.

Packages and the File System

Packages do not have unique file-system objects, as service programs and classes do. Instead, there are rules for the location where classes inside a package must exist in the file system. Each dot-separated part of a package name must correspond to a directory in the file system. This is the convention that the Java compiler and runtime follow when looking on the disk for classes in your package. For example, if you have a package named "com.bob.myPackage," your file directory structure must look like Figure 2.14.

Figure 2.14: A file directory structure.

All of the classes inside the com.bob.myPackage package must exist in the directory com\bob\myPackage. Notice that each dot-separated part of the package name becomes a separate subdirectory of the same name. Also, keep in mind that these names are very case-sensitive, so the file system's directory names must match the package names in case.

CLASSPATH Environment Variable

To run Java applications, you must set the CLASSPATH environment variable on your system. The CLASSPATH environment variable is where you list all the directories that the Java compiler and virtual machine will search when looking for a class. On Windows, this is a semicolon-separated list of fully qualified path names. On UNIX, OS/390, and OS/400, this is a colon-separated list of fully qualified path names. The paths are Hierarchical File System names on OS/390, and Integrated File System names on OS/400. Both the Java runtime and the Java compiler use this path to find the classes that your code references. However, the JDK-supplied classes are always found, even if there is no CLASSPATH. Very often, you include a dot (for the current directory) as the first entry on the CLASSPATH, so the current directory is searched first.

When working with packages, you specify the root directory on the CLASSPATH. Java automatically searches the child directories of all directories specified on the CLASSPATH, according to the package naming. On Windows, then, your CLASSPATH statement in your autoexec.bat file might look like this:

SET CLASSPATH = .;c:\myJava

With this CLASSPATH, when Java encounters an import statement like "import com.bob.myPackage.*;" it will search for subdirectories .\com\bob\myPackage and c:\myJava\com\bob\myPackage. It searches underneath each directory specified in the CLASSPATH environment variable. If it finds these subdirectories, it searches there for any classes that your code references.

Preparing Packages for Distribution

When your packages are ready for distribution, you may distribute them in their directory structure, or you may "zip" them into a single ZIP (compressed) file or JAR (Java archive file) file, from which the JVM (Java virtual machine) can read directly. This process involves a lot of TLAs (three-letter acronyms)!

ZIP Files

ZIP is an industry-standard compression technology initially created by PKWare, Inc. You can use any ZIP-capable tool, such as PKWare's PKZIP or the commonly used Windows-only WINZIP utility (www.winzip.com), to zip up, or compress, your subdirectory tree. The result is a single file that ends with the extension .zip.

You can give the ZIP file any name you want, as long as you retain the extension .zip. Users of your code do not have to decompress, or unzip, the file. They can set up their CLASSPATH environment variables to point directly to the ZIP file. For example, Windows 98 users might edit their autoexec.bat files to contain this line:

SET CLASSPATH = .;c:\myJava;c:\myJava\myPackage.zip

Note that you name the file, not the directory. The JVM's class loader will load classes directly from the ZIP file, if they are found there.

JAR Files

JAR files make use of ZIP compression technology, but they also use the all-Java, all-operating-systems tool jar that comes with the JDK. This tool compresses not only Java files, but any other files, such as audio and image clips, that are often required by applets. The result is a single file that ends with the extension .jar. Using a JAR file can significantly reduce the overall download time of an applet and its associated files, since all the files are downloaded at once in a compressed (smaller) format.

As with ZIP files, the JVM class loader can load classes and resources directly from a JAR file. As with ZIP files, your users can simply name the JAR file on their CLASSPATH. For example, the CLASSPATH statement in Windows might look like this:

SET CLASSPATH = .;c:\myJava;c:\myJava\myPackage.jar

You do not use CLASSPATH for applets, though. Rather, you use an <APPLET> tag inside your HTML Web page source, with a CODE parameter pointing to the applet class, and a CODEPATH parameter identifying the host-directory location of the files. When using JAR files, you also need to code an additional ARCHIVE parameter pointing to the JAR file, as shown in Listing 2.4.

Listing 2.4: The archive parameter pointing to the JAR file.
Start example
<APPLET code=CoolApplet.class
    codebase="http://www.mycompany.com/coolstuff"
    archive="cool.jar + cooler.jar"
    height=500
    width=300>
</APPLET>
End example

Here is how you would use the jar command to "jar up" all the classes in your current directory and in the com\bob\myPackage directory:

jar -cvf myClasses.jar *.class com\bob\myPackage\*.class

The -cvf option tells the command to create a new JAR file, with verbose output messages, and name the file myClasses.jar. You can specify multiple scalar or generic names for the input files. As you can see, JAR files can contain classes from the default package and from one or more named packages. Indeed, they can contain anything! For example, consider this command:

jar -cvf mySource.jar *.java

To "unjar" the contents (recall that you don't have to unjar the contents to access the Java classes inside it) use the -xvf option, as follows:

jar -xvf mySource.jar

This unpacks all the contents, preserving the relative subdirectory names, if present. To simply see what files are in the JAR file, use the -tvf option. To see all the options, just type jar without any parameters.

Because JAR and ZIP files use the same technology, you can use ZIP tools like WINZIP to work with a JAR file, and you can use the jar tool to work with a ZIP file.

Distributing Your Packages

Java code is always distributed in JAR or ZIP files. JAR files are more common because they will work anywhere, while ZIP files are foreign to systems like UNIX (which uses TAR files instead).

For applets, distribution is easy. You place your JAR files in a directory on your Web server. For OS/390, this is an HFS directory, while for OS/400 it is an IFS directory. You also include an HTML page there that points to the applets and resources with an <APPLET> tag. To update, just replace the JAR files on the server.

For applications that run on the server, it is equally easy. Again, just place the files (class, ZIP, or JAR) in whatever directory you choose, and replace them there as they evolve. All users must simply ensure that the appropriate directory is on their CLASSPATH. (For details on this, see appendix D for OS/390 and appendix E for OS/400).

For applications that run on clients like Windows, you need to use another option for distributing your Java code. Using a traditional installation package will work, but these are almost always specific to a few operating systems, requiring you to write separate installation code for each operating system you choose to support. In other words, you can write-once/run-anywhere, but you can install on only a few operating systems. The good news is that there are many written-in-Java installation packages now, such as InstallShield Java Edition, that potentially work on all operating systems.


Static Data

Both Java and COBOL support data items whose values are local to the program or persistent across the run unit. The latter type of data is called static, because of its non-dynamic nature. Let's look at how both languages enable static data.

COBOL Static Data Items

In COBOL, data items in your WORKING-STORAGE SECTION retain their value between calls to a particular COBOL program unless you take steps to clear them (for example, by using the INITIAL keyword or CANCELing the program). Compare these to LOCAL- STORAGE data items, whose memory is allocated by the runtime on every call to the program and released after, automatically.

Java Static Variables

To review, in Java, local variables are declared inside a method, and are alive only while the method is executing. There are also instance variables, which are declared at the class level, outside of any method. Their lifetime is tied to the lifetime of a particular instance of the class (object). They do hold their values between method calls, but their values are unique for every object. They are like WORKING-STORAGE data items in COBOL, but COBOL can only have one instance.

Sometimes you'll want a variable whose memory and value is tied to the class itself, not particular instances of the class. For example, you might have the class Employee and in it, the variable nbrEmployees tracks how many employees you have. This variable's value is not really tied to a particular Employee instance, but more to the class of Employee because we really only want one such variable in the whole application, not one per employee. This is called a static variable. Its memory is allocated at the time the application starts, and there is only one such allocation of memory and hence only one value-not one per instance of the class. Thus, it is a closer match to COBOL's WORKING-STORAGE data items.

In Java, you can turn any instance variable into a static variable by specifying the modifier static on the variable's declaration, as follows:

static int nbrEmployees = 0;

You cannot specify this modifier on local variables in a method, only on class-level variables. Code can directly access a static variable without requiring an object-you simply qualify the reference with the name of the class, as in this example:

class TestStatic
{
    static int counter = 10;
}
....
TestStatic.counter = TestStatic.counter + 1;

For this reason, static variables are referred to as class variables rather than instance variables. They are scoped to the class itself, not to a particular instance of the class (object). When an application starts running, the runtime allocates memory for all static variables in all referenced classes. That is why you can reference these with a class name versus an object name. You do not need to allocate an object, since these variables already have memory allocated for them.

Although it is legal to use an object reference variable for qualification rather than the class name, it is not considered good form, since the next programmer to look at your code might not realize that the referenced variable, is a static versus an instance variable. Since there is only one piece of memory allocated for each static variable, any changes to them affect all that use it. Consider the example in Figure 2.15.

Click To expand
Figure 2.15: Memory allocation for static and instance variables.

In this example, the class Employee has one static variable (nbrEmployees) and four instance variables (number, name, address, and startingYear). Note that nbrEmployees is defined as public so other code can access it directly, but the other variables are private so only code in the methods in this class can access them. (That code is not shown.) Also note that the name and address variables are of type String, which we haven't discussed yet. Suffice it to say that String variables are equivalent to multiple-digit alphanumeric data items in COBOL. Also note how the names of each variable nicely line up in a column-you can do this in Java because the language is completely free-format.

The important thing to see in this example is that the memory for the static variable nbrEmployees is allocated once and only once, when the application starts. Conversely, the memory for the instance variables is allocated each time you use the new operator. Static variables have exactly one memory allocation, while instance variables have zero or more.

Finally, note that we didn't initialize any of the variables by specifying= value; in their declaration. If you do not do this in Java, it will initialize the variables to appropriate val- ues for their types when memory is allocated for them. This is not true for local variables defined in a method; these must be explicitly initialized before using them, or your code will not compile. As with all programming languages, you are encouraged to always ini- tialize all variables.

As another example, imagine that you have a class called Customer, containing variables about the customer's information, such as company name, address, contact name, and so on. If this class represents a record in a database, you will probably need a unique key variable, such as "customer number."

Each time a new customer object is created, you want to automatically assign the next available number to that customer. To do this, you define a static variable in the Customer class named nextNumber, and initialize it at program start time to a number stored in the database somewhere. Then, whenever a customer is created, you assign it that value for the customer number, and increment the customer number (and possibly update the database value as well).

Java Static Methods

Java allows not only variables to be defined as static, but also methods themselves, as shown in Listing 2.5.

Listing 2.5: Defining methods as static.
Start example
class TestStatic
{
    static int counter = 10;
    // static method…
    static int getNextCounter()
    {
        counter = counter + 1;
        return counter;
    }
}
End example

What does it mean to have a static method? It means, like class variables, that the method can be used (called) directly without needing an instance of the object:

int value;
value = TestStatic.getNextCounter();

These are called class methods. They are very useful because, as you recall, Java does not allow you to write standalone functions. All code must exist inside methods, which, in turn, must exist inside classes. If you do have a helper function that does not really need to be associated with a unique instance of a class, you can define it as static in a Helper class, for example, and use it directly as though it were a standalone function. The only difference is that you must qualify each reference to the helper function with the name of the class, but at least you are not required to instantiate an instance of that class.

Static methods can only operate on local variables in that method, and on static variables in the class. They cannot reference instance variables in the class. This makes sense because the method can be used without an instance of the class having been allocated. Class variables and class methods give you the benefit of traditional WORKING-STORAGE data items in COBOL, and the benefit of name scoping that a class offers. Because the variables and methods are always qualified by class name, you can safely reuse that variable or method name in other classes. Class methods (that is, static methods) are similar to standalone COBOL programs, in that they are self-contained functions that take all the information they need (if any) via parameters, and can be called anytime, anywhere, without the need for class instantiations.

COBOL programmers who are in the early stages of learning Java often have trouble initially understanding the differences between static methods and non-static methods. They are just not clear about when to use one versus the other. Here is some help with the concept: if your method will use any instance variables, it should not be static. If, however, your method can do its job without using any of the variables in the class (as opposed to local variables in the method), it may as well be static. The example methods, shown in Listing 2.3, should have been defined as static because all of the information needed is passed in as parameters. There isn't a dependency on any instance variables in the class.

The tough part really comes when thinking of a non-static example. In order to "think OO" you need to think about writing classes and methods that act on instance variables in the class rather than always taking their information in as parameters. For example, the max method might be part of a class called YearsOfService, and that class could have an instance variable called years defined as type int. It might then also have a max method that takes only one parameter and then compares the parameter to the years instance variable, as in Listing 2.6.

Listing 2.6: Classes and methods that act on instance variables in the class.
Start example
public class YearsOfService
{
    int years; // instance variable

    public void init(int years)
    {
        this.years = years;
    }

    public int max(int compareTo)
    {
        int retval = 0;
        if (compareTo > years)
            retval = compareTo;
        else
            retval = years;
        return retval;
    }
} // end class YearsOfService
End example

In this sample code, our max method compares the given value to the instance variable value and returns the greater of the two. If you find yourself writing only static methods that do not use instance variables, you are probably still thinking too procedurally. In- stead, you need to think about how you can better use instance variables in your class in order to store state information.


Name Scoping And Qualification

Both Java and COBOL allow some rules and control over which other code has access to a given data item. Let's review this data-item scoping in COBOL, and then discuss how you can accomplish it in Java.

COBOL Scoping and Qualification

COBOL allows you to explicitly qualify name references using the OF syntax-for exam- ple, qualify data-item names within their parent group name, or paragraphs within their section. This is needed in COBOL only to help the compiler explicitly determine which name you want in ambiguous situations.

The use of qualified names in COBOL in no way allows you to get at names that would not otherwise be available to you. You can only access names that the COBOL compiler can find by looking up the hierarchy as far as the compilation unit, which is the COBOL source program. If the name is not found there at least once, you cannot access it. If it is found more than once, you must qualify the reference to it. Even though data items in other programs can be accessed, this is only possible if they are defined in this program too, as EXTERNAL data items.

Java Scoping and Qualification

In Java, you can also explicitly qualify name references. You can qualify a variable or method name with an object name or class name if it is static. You can qualify a class name with a package name either explicitly or implicity, using import statements. Qualification is done using dot notation, as in "package.class" or "class.member." You separate the name from its qualifying object or class name with a dot, or a class from its package name with a dot, for example:

// package name qualification
myPackage.MyClass myObject  = new myPackage.MyClass();
// object name qualification
myObject.setMyVariable(10);
// class name qualification for static members
MyClass.myStaticVariable = MyClass.myStaticVariable + 1;

However, whereas COBOL uses qualification only to resolve ambiguous names, Java does this and more. By using qualification in Java, you can potentially access any class, method, or non-local variable from anywhere in the application. That is, qualification in Java provides the ability to access names that are outside of a compilation unit, which is always the class.

Any name inside a class has non-qualified access to all names in the current scope or above, extending up to the class level. The class itself is, by default, scoped to the package it is in-any code inside the package can refer to that class name without qualification (className versus package.className).


Name Access Rights

Theoretically, then, the ability to explicitly qualify names with their unambiguous scope allows all Java code access to all classes, variables, and methods; you need only qualify the reference sufficiently for Java to explicitly find that name. In general, however, this is considered bad form in object-oriented languages such as Java.

One of the tenets of OO is encapsulation-giving users of your package "black box" access to only selected methods, variables, and classes. This allows you to hide the implementation details, and subsequently change them in further revisions without worrying about breaking existing user's code. Restricting creative users from accessing your "internal" variables, methods, and classes is important for this goal. Thus, while users could access any variable or method in theory, in practice you should restrict access to some of these.

COBOL Access Rights

COBOL, too, allows restrictions on who can access what names. For example, through the use of the EXTERNAL and GLOBAL clauses, you can explicitly decide what data items the public is allowed to see. For the programs themselves, you can determine who is allowed to CALL them from the outside when you use reusable objects. With DLLs (OS/390) and service programs (OS/400), COBOL actually allows you to divide the world into two camps: the other programs within the DLL or service program (classes within your package), and the users of the DLL or service program (users of your package). By default, only programs in a DLL or service program can call one of the programs inside the DLL or service program.

By using the EXPORTALL compiler option on OS/390 and binding source on OS/400, you identify the programs that are open to access by the end-user world of your service program or DLL. Specifically, on OS/390, only programs compiled with EXPORTALL can be called by programs not in the DLL. On OS/400, only programs named in the export list of the binding source for the service program can be called by programs not in the service program. Note that in COBOL, access to programs in a service program or DLL is managed by the LE or ILE environments, while access to EXTERNAL data items is managed by the COBOL run unit. This means that no program outside of a service program or DLL can access any of the data items inside any of the programs inside a service program or DLL. Only program names are "exported," not data item names.

Java Access Rights

In Java, as with COBOL, you can divide the world up into the classes that make up your package, and the outside classes that use the classes in your package. By default, all classes, class and instance variables, and methods are accessible by all the classes in the package-and no other code outside of this. This is called package access. This is equivalent, then, to EXTERNAL data items and unexported programs in COBOL DLLs and service programs.

You can further qualify the level of accessibility in Java by using these access-control modifiers in the definitions of your variables and methods:

  • The public modifier. The world, including the end-user classes, is allowed access to this class, variable, or method.

  • The private modifier. Only the code inside this class is allowed access to this variable or method.

  • The protected modifier. Only this class or classes that extend this class (as discussed in chapter 9) are allowed access to this variable or method.

Notice that classes, like variables and methods, have package access by default, if no modifiers are specified. You cannot specify all of the modifiers for classes, but you can specify public for a class, indicating that it is usable by the world at large. If you use somebody else's package, you are permitted to use only public classes in that package. Inside each of those classes, you are allowed access only to the variables and methods defined as public.

As shown in Table 2.4, this is equivalent to the exported programs in an OS/390 DLL or OS/400 service program. All unexported program names are available to all other modules in the DLL or service program, while only the exported names are available for users of the DLL or service program (public access).

Table 2.4: Comparing Java Access Modifiers to COBOL Program Access in DLLs or Service Programs.
Java | COBOL
package (default) | Not compiled with EXPORTALL for OS/390; not listed in binding source for OS/400.
public classes | Compiled with EXPORTALL for OS/390; listed in binding source for OS/400.
private variables and methods | No EXTERNAL or GLOBAL keyword on data items.
protected variables and methods | Not applicable.

Getters and Setters

It is considered very good form in Java and other OO languages to define all your instance variables as private or protected (as discussed in chapter 9) to prevent direct external access. Instead of allowing direct access, you supply the getXXX and setXXX methods for retrieving and setting the variables. These methods are affectionately known as getters and setters, and more officially as accessors and mutators.

The reason for using these methods has to do with encapsulation. (You'll hear the term "encapsulation" a lot in this book.) Variables are almost always implementation details that are best left hidden. This gives you the flexibility to later change the variable type, length, name, and so on, while keeping the get and set method interface constant, and therefore not disrupting any other code.

The use of get and set is also a matter of data integrity. Only your class code should have direct access to the variables; you do not want to run the risk of someone changing them to unexpected values. Think back to the Stack class example earlier in this chapter. It has a topIndex variable that represents the index of the top entry in the stack table. No code but the Stack class code (in its push and pop methods) should ever change this! This is similar to your database, and how end users are restricted from editing or updating the data directly. Instead, they must call your COBOL program to access the data. Only by restricting access to your data and your variables can you ensure that the state of the data is always valid. Furthermore, only through this means can you isolate users from implementation changes.


Class Constructors In Java

Java class constructs are, as you have seen, similar to COBOL modules. Typically, you define one per source file, and it contains variables and methods (nested programs). The important differences you have seen so far are that classes have methods that can be called directly by code inside other classes, and allow multiple instances (objects), each with its own variable contents.

There is another important difference. Classes can have a special method called a constructor. A constructor is a method in the class that the compiler and runtime recognize, and that has the same name as the class. Unlike regular methods, constructors do not specify any return type on their definition line because they cannot return values.

What does a constructor do? It is called by the Java runtime when the object is first created using the new operator. This is your opportunity to initialize variables and do any setup that your class requires and that your methods assume.

Because you can initialize your class-scoped instance variables when you declare them, you might wonder what the value of a constructor is. There are at least three reasons why you might use a constructor:

  • Complex variable assignments that cannot be easily made in declaration assignment statements.

  • Initial state operations or logic, such as opening a file.

  • User control (programmer user, that is) over the initial instance variable values. This is done by letting users pass in parameters that you use to initialize your variables.

You can also have more than one constructor. Because they must all have the same name as the class, the difference is in the number of parameters. A default constructor takes no parameters, but you can define other constructors with as many parameters as you need. Recall that Java allows you to overload methods-that is, supply more than one method with the same name, as long as the number or type of parameters are different. This applies nicely to constructors. You may have versions of your constructors that take differing parameters, giving users the freedom to use defaults or specify explicit initial values.

Imagine, for example, that you have defined a class called Server, which connects to a remote server from a Java client. It might have a number of constructors: one that takes a remote system name, user ID, and password for connecting and signing on; one that takes only a remote system name and hard-codes a user ID and password; and one that takes no parameters and hard-codes all three variables.

The users specify the parameters to a constructor as part of the new operator statement, as follows:

Server myHost = new Server(); // take defaults
Server myHost = new Server("SYSTEM1", "BOB", "ABABA"); // supply
defaults

One constructor can call other constructors, allowing you to abstract-out common code. You do this by calling the this() method, with the appropriate parameters, if applicable. The example in Listing 2.7 is a typical use of this, where only the constructor that takes all the parameters does the real work, while the rest with successively fewer parameters call the one "full" constructor and pass in hardcoded defaults for the missing parameters. Note that ctor is a common abbreviation for constructor when writing comments.

Listing 2.7: An example of one constructor calling other constructors.
Start example
class Server
{
    private String systemName; // name of system to connect to
    private String userId;     // User-id for logging in
    private String password;   // password for logging in
    boolean connected = false; // boolean is a built-in type

    Server() // default constructor
    {
        this("SYSTEM1","PHIL","GREATGUY"); // call 3 parm ctor
    }
    Server(String systemName) // constructor with one parameter
    {
        this(systemName, "PHIL", "GREATGUY"); // call 3 parm ctor
    }
    Server(String systemName, String userId, String password)
    {
        this.systemName = systemName; // instance var = parameter
        this.userId     = userId;     // instance var = parameter
        this.password   = password;   // instance var = parameter
    }
    // rest of class not shown. . .
} // end Server class
End example

Remember this: you do not explicitly code a call to a constructor. Rather, you code the new operator as usual, possibly passing in parameters, and the Java runtime implicitly calls the appropriate constructor for you. That is, the runtime looks for a constructor method in your class that matches the number and type of parameters specified with the new operator, after the name of your class and between the parentheses that follow. It then calls that method for you, passing the new parameters to the method. Constructors are really just a shortcut, the equivalent of supplying a method called init, say, that you would instruct users to call in order to initialize your object's state or variables.

Constructors, like methods, allow you to specify access modifiers. These modifiers have the additional responsibility in a constructor of affecting who is allowed to instantiate your class. If you specify public, this class can be created by anyone. If you specify protected, this class can be created only if it is extended by another class (as explained in chapter 9). The default is public if the class is tagged as public, and package otherwise, which means only code in this package can instantiate this class with this constructor.

There is a subtlety to constructors: if you do not define any, Java will supply a default one with no parameters that does nothing. If you supply even one constructor, however, it will not do this. If you supply one constructor that takes one parameter, and a user tries to instantiate your class without any parameters, he or she will get an error message stating that no default constructor exists. In this case, if you want to allow a no-parameter "new" operation, you must explicitly supply a no-parameter constructor as well, even if it is empty (that is, has no code).

So, is there any equivalent to a constructor in COBOL? Well, there is, sort of. It is the first paragraph in the non-declarative code in your PROCEDURE DIVISION. This is the code that gets control when your COBOL program is called; in that sense, it is similar to a constructor. It is the first code to run in your program when it is used (called), again similar to Java constructors, which are the first code to run in your class when it is used (instantiated). The differences, though, are that constructors are optional, and there can be more than one of them. Further, constructors can be called multiple times because a class can be used multiple times, by instantiating it multiple times.


Calling All Programs

You've seen a lot of the outer skin of Java by this point in this chapter. To complete this quick tour, you'll learn how to write Java applications that can be called from the command line.

Calling a COBOL Program: the Mainline

As you know, you can invoke the initial COBOL program in your COBOL executable object from the command line or from programs in other executable objects using the CALL statement. This gives control to the initial program and passes to it any parameters specified on the call and identified on the USING phrase of your PROCEDURE DIVISION.

Calling a Java Program: the "main" Method0

In the case of a Java application (and in contrast to an applet), you also invoke the Java applications from the command line, but you do so by calling the Java interpreter or virtual machine, which is done with the java command. Often, the initial class file you call will not be part of a named package, so you call it directly, as in java MyClass. (Note that the class name is case-sensitive).

To call a class that is part of a package, you must qualify the call with the package name, as in java myPackage.MyClass. In this case, Java will search the CLASSPATH environment variable for a directory that has a subdirectory named myPackage, and look inside that subdirectory for MyClass.class. It will also search any ZIP or JAR files named on the CLASSPATH, as discussed earlier in this chapter.

Can you simply call any class directly, like this? No! By the same token, you cannot call into any COBOL program directly inside an executable object, only the initial program. When the Java interpreter invokes a class from the command line using the Java interpreter, it looks inside that class for a method named main. Actually, the interpreter searches not just for any main method, but one that specifically looks like this:

public static void main(String args[])
{
...
}

Java looks for exactly one such method with exactly this signature. If it does not find such a method, you will get an error message. Of course, if there were two such methods, you would have gotten an error message at compile time (javac). The breakdown of the signature is shown in Table 2.5. Note that all parts of the signature are mandatory.

Table 2.5: Breakdown of Signature.
Part | Description
public | This method is publicly accessible.
static | This method does not require an instance of the class to be created first.
void | This method does not return any values.
main | This is the name of the method, and is case-sensitive.
String args[] | This is an array (a table, in COBOL terms) of strings, one for each parameter specified on the command line.

You will learn about the parameters shortly. Typically, this initial class, which contains the main method, instantiates classes from one or more packages and invokes them.

Because the method returns no value, no return statement is required at the end of the method. When the last line of the main method is done executing, the program ends. This, then, can be compared to the first paragraph in your PROCEDURE DIVISION. But we just compared that to a constructor, you are thinking! Well, no analogy is perfect! To refine ours a bit, think of that first paragraph code as being a constructor for your COBOL subprograms, and as being a main method in your initial COBOL program. It is not a great analogy though, since Java classes can and often do have both constructors and a main method.

Table 2.6 summarizes some of our analogies so far.

Table 2.6: Summary of Analogies between COBOL and Java.
COBOL | Java | Comments
Executable object (*PGM load module) | Main class file | File system objects where or control begins.
Reusable object (*SRVPGM or DLL) | Packages | How we package for reuse and distribution.
Entry program in execut-able object | Main class file | Main class can have any name. Often not in a named package.
First paragraph | Main method in initial pro- gram, constructor in non-initial program of executable objects | Gets control.
Other modules in execut-able object | Other classes main needs that are in same package as the main class | Other "bound" programs.
Other modules in objects | reusable Classes in other packages that main uses | Other "linked" programs.

Illustrated pictorially, the analogies look something like Figure 2.16.

Click To expand
Figure 2.16: A comparison of COBOL and Java.

Command-Line Parameters in COBOL

In COBOL, you specify the parameters to your main program object on the command line or CALL statement. The COBOL entry program defines the parameters that it expects via the USING phrase on the PROCEDURE DIVISION header, which names data items defined in the LINKAGE SECTION. For example, if you have a program in which the entry module accepts a six-character string, you might define the WORKING-STORAGE data item and PROCEDURE DIVISION header for the parameter as follows:

DATA DIVISION.
LINKAGE SECTION.
77 KEYFIELD PIC X(6).
PROCEDURE DIVISION USING KEYFIELD.

Command-Line Parameters in Java

In Java applications, you pass parameters on the command line with the java command. You specify the parameters, after the name of the Java class to invoke, with blank delimiters. Each word or token specified becomes one parameter. Each parameter is accessible to your Java main method as an entry in the args array of strings.

Unlike COBOL, every parameter is passed to Java as a string, and you must explicitly convert it to the required data type yourself. (This is covered in more detail in chapter 5.) In order to pass a string with embedded blanks as a single parameter, you must enclose the string in quotes on the command line, for example:

JAVA bob hi there "George and Phil"

This fills String args[] array entry 0 (arrays are zero-based in Java) with "hi," array entry 1 with "there," and array entry 2 with "George and Phil."

Writing to the Command Line in COBOL

Very often, it is either necessary or worthwhile to write information out to the command line. It is a time-honored debugging trick, for example, to spit out "I am here" messages to track your flow of control in a program. (This is not so widespread anymore, thanks to the proliferation of source-level debuggers, but we all still do it occasionally. Well, don't you?) Also, almost all introductory programming books start with a simple little "hello world" program that writes this string out to the screen. The first page of the book is rarely the place to introduce advanced user-interface constructs!

In COBOL, a common way to write simple output like this is by using the DISPLAY statement. Using DISPLAY, you could write a simple introductory and obligatory "hello world" COBOL program, with the additional twist that it prints whatever is passed on the command line, as showing in Listing 2.8.

Listing 2.8: A simple COBOL program to display a passed string.
Start example
IDENTIFICATION DIVISION.
PROGRAM-ID.     HWORLD.
AUTHOR.         Phil Coulthard
******************************************************
* Prototypical Hello World program, except it        *
*  writes back to the console what it has been passed*
******************************************************
ENVIRONMENT DIVISION.
DATA DIVISION.
            FILE SECTION.
WORKING-STORAGE SECTION.
        LINKAGE SECTION.
        01 PARMDATA.
            02 STRINGLEN      PIC 99 USAGE IS BINARY.
            02 STR.
                03 PARM-BYTES   PIC X OCCURS 0 TO 120
                            DEPENDING ON STRINGLEN.
    ****************************************************************
    **           P R O C      D I V I S I O N              ***
    ****************************************************************
    PROCEDURE DIVISION USING PARMDATA.
    STARTS.
        DISPLAY "HELLO THERE".
        IF (STRINGLEN > 0) THEN
        DISPLAY STR(1:STRINGLEN).
            DISPLAY "Goodbye".
        STOP RUN.
End example

Calling this program interactively with CALL HWORLD 'hi there' results in the phrase "hi there" (or whatever was passed) being printed to the console.

Writing to the Command Line in Java

In Java, the equivalent to DISPLAY is called writing to standard output. It involves using a Java-supplied class named System, and a static object variable inside of that class named out, and finally a method of out called println-which is short for "print line." Here is an example:

System.out.println("Hello world");

By now, you know about static variables, and you see here that object reference variables can also be static. In this case, out is a static object reference variable in the System class. Because the object is static, you can use it directly, without having to first instantiate an instance of System using new. Rather, you simply qualify access to it with the class name.

The method println prints a line of text to the screen-to the command line, actually, where java was invoked. It is written to a special file called standard output. This is not a file on disk. Typically, the content of the standard output file is displayed on the invoking command line (the console). In graphical applications or applets where there is no command line, however, you have to be running inside a shell that captures and displays this output in order to see it. For example, VisualAge for Java has a console window that displays any information written to standard output while the application or applet is running inside the product.

You will quickly become very familiar with System.out.println as you begin to learn Java. Most simple starter applications use it to display dynamic information to you. The method println takes any number of strings or variables, separated by plus signs (+), and displays them. This means that println automatically converts variable contents from their native type to String format, which is very useful. For objects, println does this by trying to call a method in the object named toString. You must supply this method for this to work with objects or instances of your own classes.

In Listing 2.9, then, is the Java equivalent of the COBOL program that displays the passed-in string. After compiling the code (in Windows) with javac HelloWorld.java, running it produces something like this:

>java HelloWorld "hello world!"
Input: hello world!
Listing 2.9: The Java equivalent of the COBOL program in Listing 2.8.
Start example
public class HelloWorld
{
   // main method
   public static void main(String[] args)
   {
      // print first parameter passed
      System.out.println("Input: " + args[0]);
   }
}
End example

There are several things to note about this example:

  • args[0] is the Java syntax for the first entry in array args.

  • The program will fail if you do not pass in at least one parameter because args[0] will not be defined.

  • Any parameters after the first will be ignored.

  • To pass a multiple-word input string, it must have quotation marks around it.

A more robust version of the preceding program checks to make sure that there is at least one parameter, concatenates all of the parameters in a temporary string first, and then prints, as shown in Listing 2.10.

Listing 2.10: The "hello world" program with additional functionality.
Start example
public class HelloWorld
{
   // main method
   public static void main(String[] args)
   {
      String outString;
      // build up output string
      if (args.length > 0)
         {
            int idx;
            outString = "Input: ";
            for (idx=0; idx<args.length; idx=idx+1)
            {
               outString = outString + args[idx] + " ";
            }
         }
      else
         outString = "No input given";
      System.out.println(outString);
   }
}
End example

Don't be scared! This example presents a number of functions we have yet to discuss, such as the if and for statements. This is just a morsel to whet your appetite for subse- quent chapters. Come back and revisit it later, and you will be amazed at how easy it is to read and understand. For now, though, just to convince you that it works, here are some sample runs:

>java HelloWorld
No input given
>java HelloWorld a simple test
Input: a simple test
>java HelloWorld "helloWorld!"
Input: helloWorld!

Congratulations! You have written (well, at least seen) your first running Java code!


Summary

You covered a lot of ground in this chapter, both from a COBOL point of view and from a Java point of view. You have learned that:

  • A Java application is comprised of Java source files (ending with .java) that are compiled via javac into Java classes containing bytecodes.

  • A Java source file contains one class (typically) with variables and methods that use those variables.

  • Flow of control starts with a Java main class containing a Java main method, and continues via object method calls through to other Java classes.

  • A class is used by instantiating an instance of it (an object) with the new operator.

  • Non-static instance variables and methods are accessed using an object reference variable and the dot operator for qualification.

  • Static variables and methods can be accessed directly by qualifying them with the class name.

  • Java classes are similar to COBOL programs, Java methods are similar to

COBOL paragraphs or nested programs, and Java packages are similar to COBOL DLLs (OS/390) or service programs (OS/400).

  • Java classes are usually distributed as compressed ZIP or JAR files. The Java Virtual Machine can read the classes directly from these files. JAR files are created with the jar command from the JDK.

  • To run Java applications, you must set your CLASSPATH environment variable. It contains a list of directories, JAR file names, or ZIP file names separated by semicolons (in Windows) or colons (in other systems). All must be fully qualified. Usually the first entry is a dot (.) for the current directory.

  • To run applets, use a JAR file and identify its server location with the ARCHIVE parameter of the <APPLET> tag in HTML.

  • Two Java methods in the same class can have the same name, as long as their signatures are different. This is called method overloading. A method signature is defined as the number of parameters and the type of the parameters, collectively.

  • Java classes can have special methods called constructors, which are implicitly called by the Java runtime when an object is created with the new operator. These constructor methods have the same name as the class and do not specify a return type. You can have multiple constructors as long as their signatures are different. Java will call the appropriate constructor that has the same number and type of parameters as you specify with the new operator.

  • Java and COBOL both have syntax for exporting methods or procedures for external users, or hiding them for internal use only. In Java, this is done with the method modifiers public and private.

  • Command-line parameters can be passed to a Java class, and it is easy to print out to the command line (console) in Java.

Raid the refrigerator again, take a nap, and we will rejoin you in the next chapter.


Chapter 3: Java Syntax

This chapter begins to examine Java in more detail, with a focus on the language's core syntax. Subsequent chapters build on this by moving into other core areas of the language. This chapter also introduces you to the basics of the Java language, includ- ing the syntax of comments, statements, expressions, and operators.

I Want To Be Free

Unlike COBOL, which has columns for Area A (8 to 11) and Area B (12 to 72), Java is a free-form language. The compiler ignores all redundant white space, including line breaks. For example, this:

void myMethod(int parameter1) { return; }

is equivalent to this:

void myMetzhod(int parameter1)
{
return;
}

Freedom in Java, as in other realms, is best enjoyed by observing a few rules. To aid in programmer readability, you'll probably want to set some standards in your shop, related to indentation and style rules. The rules themselves are not as important as using them consistently, so everyone can effectively read everyone else's code.

All statements in Java end in a semicolon. This is a result of the free-form style of the language: the compiler needs a definitive way to determine the end of one statement and the start of another. Thus, in the previous example, the return statement ends in a semicolon. White space is not important, so the following three statements are all equivalent:

return;
return     ;
return
;

The next pervasive syntactical note is that curly braces are used in Java to delimit the beginning and end of blocks. In the previous example, the body of the method myMethod starts with an opening brace, {, and ends with a closing brace, }. As you will see, these braces are used to delimit classes, methods, and bodies of statement blocks. Every opening brace must eventually be followed by a closing brace. So, "brace" yourself to see many semicolons and a lot of free space.


Comments

As a responsible programmer, you always document your code with meaningful comments. (Don't you?) In this section, you will learn how to continue that tradition in Java.

In COBOL, to specify a comment, you either put an asterisk (*) in column 7 for a line comment, use comment areas of the IDENTIFICATION DIVISION, or use the comment area of every line (beyond column 72). In Java, you also have three types of comments at your disposal, as shown in Table 3.1.

Table 3.1: Comment Types in Java.
Comment Type | Description
/* comment */ | Multiline comments surrounded by /*) and */.
// comment | Single-line comments using the double slashes.
/** comment */ | JavaDoc comments surrounded by /** and */.

Like C and C++, Java uses a forward slash immediately followed by an asterisk, with no intermediate blanks to indicate the start of multiline comments, and the reverse (an aster- isk immediately followed by a forward slash) to end them:

/* this is a multiple
line comment */

Everything between the comment delimiters is commented out, no matter how many lines they cross. This form of markup is often used for comment blocks such as the following:

/**
Please read these comments *
as they are very important! *
**/

You cannot nest these block comments inside each other, as in

/* outer /* inner */ outer */

Also, because it is easy to forget the ending delimiter, even the best of us have been known to accidentally comment out large chunks of code in Java. A good editor (such as those in VisualAge for Java, VisualAge for COBOL, or in CODE/400) will eliminate this problem. These editors show comments in a different color than the rest of the code, so you can see at a glance when lines have been inadvertently commented out.

Double slashes are used for a single-line comment. All text after the double slashes is ig- nored to the end of line, as follows:

// show comment example
int myVariable = 10; // define and initialize variable

The final kind of Java comment is a refinement of the multiline comment. These are called JavaDoc comments, which use a double asterisk to start the comment, as follows:

/** Hey you, *
* read this! *

This comment is identical to the multiline type, with two exceptions: it can also be used by the javadoc command to generate API documentation from the code, and it recognizes special tags within the code to aid the generated output. The following section describes this comment style in more detail.

JavaDoc Comments

We all know the importance of documenting our code, both internally and externally. Typically, it's done to benefit the poor followers who have to maintain the brilliant (yet indecipherable) code we leave behind. In Java, the need for comments is even more pressing because Java is all about reuse of code, and such reuse is accomplished by supplying compiled classes and packages to others for their usage. Since the code you supply is compiled, these users have no access to the internal comments. They must rely on some form of external documentation to tell them how to use the classes and, most importantly, what the methods are and exactly what parameters they expect. What the methods do and what they return is also of vital importance to other programmers hoping to use it, else how will they know when to call them, and how to call them.

Java supplies an easy way to embed important API informational comments right in your code where it belongs, and yet extract those comments to produce external HTML documentation. This is a very good thing! It means that these comments can be both internal and external, and that a consistent format for the information can be achieved. Anyone who has written separate external information to document their code knows how quickly such "out of sight, out of mind" documents become out of date. And choosing a word processing or other format for these comments can be troublesome for readers who don't have the chosen software.

The JDK (Java Development Kit) includes a utility command called javadoc to generate API documentation from source code that contains JavaDoc comments. For example, in order to generate documentation for a class called Extract, you would type the following:

> javadoc Extract.java

The argument passed to javadoc is a series of Java package names and/or Java source file names. The javadoc command parses the JavaDoc-style comments contained in the source files and produces a set of HTML pages describing the classes, interfaces, constructors, methods, and class-level variables. The output of javadoc depends on whether you are using JDK1.1.x or JDK 1.2.x or higher, since the output files changed for JDK 1.2.0. Specifically, javadoc produces the files shown in Table 3.2 for JDK 1.1.x, all in one directory.

Table 3.2: JavaDoc 1.1.x Files.
File type | Description
Main entry point | The main starting point is a file called index.html.
Package list | An index list of all packages in a file called packages.html.
Index | An index of all classes and members in a file called AllNames.html.
Class hierarchy | A list of all classes and their hierarchy in a file called tree.html.
List of methods | One file per class that lists the class and its methods.

For JDK 1.2.0 (which was branded "Java 2") or higher, the output is the files listed in Table 3.3, in multiple subdirectories (one subdirectory tree per unique package name).

Table 3.3: JavaDoc 1.2.x Files.
File type | Description
Main entry point | The file index.html now uses HTML frames.
Package list | An index list of all packages in a file called package-list.html.
Index | An index of all packages, classes, and members in index-all.html.
Class hierarchy | A list of all classes and their hierarchy in overview-tree.html.
List of methods | One file per class that lists the class and their methods. This is found in a subdirectory tree that matches the name of the package containing the class. Each dot-separated part of the package name becomes a subdirectory, so my.package becomes my\package.

When javadoc parses a documentation comment, leading asterisks (*) on each line are discarded. For lines other than the first, blanks and tabs preceding the initial asterisk character is also discarded.

The following example illustrates JavaDoc comments in a Java source file:

/** This is the </U>scan package</U>
*  this is the second line.
*  @author George & Phil
*  @version March 22, 2000
*/

Comments can have embedded HTML tags, so <U>scan package</U> produces the underlined text scan package. The first sentence of each comment should provide a concise, yet complete, description of the declared entity. This sentence ends at the first period. It is important to be concise and describe the complete entity because javadoc uses this as the summary for the package, class, or member at the top of the HTML file. (Notice that the term member refers collectively to methods and class-level variables.) By default, javadoc only produces documentation for public classes and public or protected members, but this can be overridden with the -public, -package, or -private options to the command.

Besides parsing HTML tags, javadoc recognizes a special set of tags that start with the at sign (@). These tags provide additional information, such as "see also" references, an author name, or a version number for this source. The example used two tags: @author to describe the author names, and @version to identify the date for this version of the files. Table 3.4 summarizes the tags that javadoc recognizes.

Table 3.4: . JavaDoc Tags.
Tag Name | Description
@author | Formats the given name as the author name. A comment may contain multiple @author tags. This tag can only be used in a class or interface documentation comment, not a method or variable comment.
@see | Adds a hyperlinked "see also" entry. The class name can be qualified by the package name. To reference a method or variable's javadoc documentation, use # after the class name, as in@see pack-age.class#member
@version | Formats the given version information. A comment may contain at most one @version tag. This tag can only be used in a class or interface documentation comment, not a method or variable comment.
@since | Adds a "since" entry. This tag means that this change or feature has existed since the specific release number of the software.
@deprecated | Identifies an obsolete method. You should list the replacement. If the member is obsolete and there is no replacement, the argument to @deprecated should be "No replacement."
@return | Contains the description of the return value.
@param | Formats the given parameter name and its description in the parameters section of a method description. The description may be continued on the next line. Only one tag is allowed per parameter.
@throws | New in JDK 1.2, describes an exception thrown by this method, in the format@throws class-name description Only one tag is allowed per exception.
@serial, @serialData, and @serialField | New in JDK 1.2, describes information related to "serializing" this class to disk, an advanced topic covered in chapter 14.
{@link xxx} | New in JDK 1.2, identical to @see but generates inline hypertext links versus a separate "see also" section.

Each tag except @link must start at the beginning of a line. Try to keep tags that have the same name together within a comment. For example, put all @author tags together so that javadoc can tell where the list ends. Listing 3.1 is an example of a documented class.

Listing 3.1: A documented class.
Start example
/**
* A cool class. *
* @author Phil Coulthard and George Farr
* @version 1.0
* @see YourClass
*/
public class MyClass
{
    /**
    * Constructor
    */
    public MyClass()
    { } // empty for now
    /**
    * Shows a message
    * @param message The message string to show
    * @return void
    * @see MyClass#myMethod2(String message)
    */
    public void myMethod(String message)
    {
        System.out.println(message);
    }
    /**
    * Shows a message in quotes
    * @param message The message string to show
    * @return String object containing quotes
    * @see MyClass#myMethod(String message)
    */
    public String myMethod2(String message)
    {
        String newMessage = "'" + message + "'";
        System.out.println(newMessage);
        return newMessage;
    }
} // end class MyClass
End example

After creating a new html subdirectory to hold the results of javadoc, run javadoc against the class source file using the directory option (-d) to put the output in the new subdirectory:

> md html
> javadoc -d html MyClass.java

Then, for JDK 1.1.x only, you need to copy the image files that the generated HTML uses to an images subdirectory of the new html subdirectory (JDK 1.2.0 or higher does not use image files in its generated JavaDoc HTML):

> cd html
\html> md images
\html> copy c:\jdk1.1.4\docs\api\images\*.* images

Finally, you can open one of the generated HTML files (in the html directory) by typing start MyClass.html. In our example, this causes Microsoft Internet Explorer to open the file. Part of the result is shown in Figure 3.1.

Click To expand
Figure 3.1: A generated HTML file.

Due to space constraints, the whole result isn't shown here. However, Figure 3.2 shows another part of it-what you see when you click on the myMethod link in Figure 3.1.

Click To expand
Figure 3.2: The result of clicking the myMethod link.

For more information about javadoc, consult the JDK documentation. Specifically, look for the file \docs\tooldocs\javadoc\index.htm in your JDK directory.


Variable Naming

An important feature shared by COBOL and Java is the ability to reference data that is stored in memory by using descriptive symbolic names (called data items in COBOL and variables in Java) rather than numeric memory-cell addresses. Each data item or variable in your program must have a unique name. COBOL limits data item names to 30 characters and ignores case. Java variable names have unlimited length, and are very case-sensitive.

Case-sensitivity is a key point that the COBOL programmer must consider. All referenced and declared names must match letter-for-letter. Furthermore, the source file name must match verbatim the defined class name inside the source file, including the case. Otherwise, the compiler will generate errors that you might not be able to diagnose easily. It bears repeating that, as a Java programmer, you must be especially sensitive!

Naming Syntax for Variables

COBOL data item names contain letters, digits, and hyphens only, and at least one character must be alphabetic. For Java variable names, the first character can be any valid letter, the underscore, or the dollar sign. The remaining characters can include both letters and digits. Furthermore, Java names can include Unicode characters as well, if you so desire.

Keywords

Keywords are identifiers or names that are reserved by the language, and that carry special meaning within it. For example, in COBOL, the VALUES and LIKE keywords tell the compiler something important. The COBOL reference manual describes all of the keywords reserved by the language and hence not available for your use as data names. These include figurative constants, such as SPACE and ZERO.

Java and all languages use keywords. The designers of each specific language decide on the keywords' names, usages, and meanings for that language. Because keywords and figurative constants have special meaning, you cannot use them as names of variables in your programs. Table 3.5 lists the reserved keywords in Java.

Table 3.5: Reserved Words in Java.
abstract | default | goto | null
synchronized | boolean | do | if
package | this | break | double
implements | private | throw | byte
else | import | protected | throws
case | extends | instanceof | public
transient | catch | false | int
return | true | char | final
interface | short | try | class
finally | long | static | void
const | float | native | super
volatile | continue | for | new
switch | while |  |

The two special words const and goto are not used in Java. Although they might be assigned meanings in future versions of the Java language, the real reason for their reserved status is to keep C and C++ programmers from accidentally using them.


Comparing Anatomies

To further compare COBOL and Java, let's review at a high-level the anatomy of both a COBOL program and a Java class. Anatomies here describe bodies of code.

Anatomy of a COBOL Program

A COBOL program consists of divisions, which can consist of sections, which can con- sist of paragraphs, which can consist of sentences, which are made up of one or more statements. Statements, or a series of statements that end with a terminator (a sentence), are the interesting elements that make up our executable logic.

Statements come in four categories in COBOL:

  • Imperative statements are used for unconditional actions, such as COMPUTE.

  • Conditional statements are those whose subsequent actions are determined by a truth-value of a condition, such as IF, EVALUATE, or ADD… ON SIZE ERROR.

  • Delimited scope statements are explicit scope terminators such as END-ADD. A scope terminator is provided for all verbs that can have conditional phrases. An explicit scope terminator on a conditional statement makes it an imperative statement.

  • Compiler-directing statements tell the compiler to do something, such as PROCESS.

Imperative and conditional statements often include expressions. Expressions come in two varieties:

  • Arithmetic expressions are numeric identifiers, literals, figurative constants (ZERO, SPACE), and intrinsic function calls, optionally combined with arithmetic binary operators (+, -, *, /, **), or unary operators (+,-). Expressions can be placed inside parenthesis for precedence, and one expression can be an operand itself within a larger expression. These are most often used on a COMPUTE statement.

  • Conditional expressions evaluate to true or false, which determines among alternative flows of control in EVALUATE, IF, PERFORM, and SEARCH statements. Conditional expressions can be simple, or complex, combining multiple simple expressions with AND or OR, or negating a simple expression with NOT. Simple expressions are those of class, condition name, relation, sign, or UPSI switch status. Examples of these are IF MYVAR IS NUMERIC, IF TEEN-AGER, IF MYVAR IS GREATER THAN 10, IF MYVAR IS POSITIVE, and IF MYVAR, respectively.

Now, consider Java's anatomy.

Anatomy of a Java Class

A Java class consists of a class definition, which consists of variable declaration statements and method definitions. Method definitions consist of variable declaration statements and other statements. Again, the interesting parts are the statements, whether they are variable declaration or others.

Statements in Java come in different varieties than they do in COBOL:

  • Variable declaration statements are used to define identifiers or variables. Variables are defined by a type, followed by an identifier name. Nonlocal variable declarations can also, optionally, start with a modifier, such as public. They always end in a semicolon, and can include an optional initialization phrase, as in int x = 10;.

  • Expression statements are expressions that become statements by ending them with a semicolon. These are assignment expressions, pre/post increment/ decrement expressions, method invocation expressions, and class instance creation expressions. You will learn more about these later. Note that methods that return nothing can't be called in expressions, so they must be called in a standalone statement. However, even methods that do return something can be called standalone, in which case the return value is ignored. For example, this is a valid standalone statement: myObject.myMethod();.

  • Block statements provide a way to combine multiple statements into a single statement, using curly braces, {}, as block delimiters. Block statements can be used anywhere single statements are allowed. Blocks can also contain variable declaration statements.

  • Empty statements are those with a simple semicolon that do nothing.

  • Labeled statements are preceded by a label identifier and a colon, so that they can become the transfer-of-control targets of break and continue statements (covered later).

  • Other statements in Java are subdivided into decision, loop, and transfer statements that affect the flow of control. These are covered in the next chapter. Note there are no compiler directives as COBOL has, although you can think of package and import statements as being compiler directives.

As with COBOL, you can have more than one statement per physical line, but this is considered bad style. Also, like COBOL, Java expression statements and labeled statements can contain expressions.

Expressions in Java, unlike COBOL, are divided into the following two categories:

  • Fetch expressions are executed just for the value they result in, which is used in place of a simple variable or literal. These are similar to COBOL's expressions, and similarly can be divided into conditional and nonconditional. Conditional expressions evaluate to true or false, and nonconditional ones evaluate into another type, such as numeric.

  • Computational expressions are executed for their side effects, such as a change to a variable or the action done by a method.

All of these statement types and expression syntax are covered in more detail shortly. Remember, though, that white space, such as blanks, is not important to the Java compiler, as it is to the COBOL compiler. Since X=A+B is the same as X = A + B, you can decide what you like best.


Statements

Programmers use statements to control the sequence and frequency of execution of different segments of a program. Java is a more free-form language than COBOL. There are no Areas A and B, or any other demarcation of a line by column numbers. There are, however, still rules in Java about what makes up an executable statement, what indicates a block of consecutive statements, and what ends an executable line of code. The following sections cover four different types of statements in Java:

  • Expression

  • Block

  • Empty

  • Labeled

Expression Statement

With the simple arithmetic operators ( +, -, *, /), you can build simple arithmetic expressions in both Java and COBOL. For example, X + 1 and Y + 2 / 3 are simple expressions, with X and Y being data items or variables. COBOL and Java would be very limited if they used only these four basic operators, however. As you will see later in this chapter, both COBOL and Java have a rich set of operators for creating expressions. The following examples demonstrate different expressions that you might use in your programs:

1.8 / 3
X+2-3+I
amountDue > 0

You can also assign expressions to variables. To do this in Java, you use a variable on the left side of the assignment operator (equals sign), as follows:

myvalue = 1.8 / 3
result  = X+2-3+I

Notice in these examples that the white space between the operators is generally not important, although consistency does aid readability. The examples here shows how you might code some sample expressions in your program. However, they are not syntactically correct for Java statements. Can you guess why not? To turn these into valid expression-assignment statements you must (as we mentioned in the introduction to this chapter) end each statement with a semicolon. In Java, the semicolon is a statement terminator, similar to COBOL's period for ending statements. And that is our final statement on semicolons, period!

For COBOL, arithmetic expressions are valid only when used with the COMPUTE statement, with its relatively free-form expression syntax. COBOL's COMPUTE does have very explicit spacing rules, unlike Java. Further, COMPUTE allows only arithmetic expressions, unlike Java, which allows other expression types, such as logical.

In summary, an expression statement consists of an expression that is executed for its side effects or its computed value. For instance, the previous example showed assignment expressions where the expression on the right was executed for its value. A Java method call, on the other hand, might be executed just to achieve the side effects of the call, and the resulting value, if any, would be ignored.

Block Statement

A Java block statement groups together a sequence of one or more statements. The group is delimited by enclosing it with a starting brace { and a closing brace }. A semicolon is used to end each statement within the sequence, as usual. There is, however, no semicolon after either brace.

Braces can be used to group declarations and statements together into a single entity, or block. They are syntactically equivalent to the braces that surround the statements of a class or method. You can also group multiple statements together after an if or while structure, or just in a standalone block. For example, you could block the expression statements shown in the previous example as follows:

{
myvalue =  1.8 / 3;
result  =  X+2-3+I;
}

This block is syntactically correct; the compiler will not issue any errors for it. However, it would be more typical for you to block a group of statements in order to make them look equivalent to a single statement. You can then apply an if or a loop condition before the block. The compiler will execute the block only if the condition is met. Another reason to use blocks is to define a set of variables locally in the block. If you try to reference one of these variables outside the block, the compiler will indicate that it is not declared. For example, the if statement syntax in Java is as follows:

if (expression)
statement;

In order to apply a condition to the previous block of code, you would code an if with its expression above the block. This indicates to the compiler that you want the block to execute only when the expression evaluates to true. To use an if condition with a block of code (more than one statement), you would code as follows:

if (expression)
    {
        float myvalue; // declare a float type variable called myvalue
        int   result;  // declare an integer type variable called result
        myvalue    =  1.8 / 3;
        result     =  X+2-3+I;
    }

It is important to note that the block is treated as a single statement. That is, either all or none of the statements inside it are executed in this example. If you only had one statement to execute after the if, you would not need the braces. For example, the following if statement is syntactically correct:

if (x == 2)
x = x+1;

You normally use a block statement when you want to execute more than one statement after an if or while condition. Notice also that braces are optional when you have one statement to execute. In other words, you could code the example as follows:

if (x == 2)
   {
      x = x + 1;
   }
Note

In the preceding examples, the double equals sign (= =) is used to distinguish comparisons such as this one from assignment statements that use the single equals sign (=). Note also that assignment statements are not allowed inside a conditional expression such as this; only expressions that evaluate to true or false are allowed.

Blocks are similar to using an explicit scope terminator like END-PERFORM in COBOL.

Empty Statement

The simplest kind of Java statement is an empty one. You now know that Java statements must end with a semicolon. What, then, happens if you have statements like the following?

if (x == 2)
   ;
for (i = 0;i < 10;i = i+1)
{
   ;
}

Are these statements syntactically correct? (Don't worry about the syntax of the for statement for now.) The answer is yes. A semicolon on its own with nothing preceding it is just another Java statement. In this case, it is called the empty statement. Empty state- ments are very rarely used, but they appear occasionally as the true part of an if state- ment when all you are interested in is the else part, for example:

if (x > 2)
    ;
else
// interesting statements

This is similar to using the CONTINUE or NEXT SENTENCE statements in COBOL (although Java allows the empty statement anywhere). This example is more elegantly coded in both languages by negating the conditional expression. Also notice that an equivalent to the empty statement is an empty block, for example:

if (x > 2)
    {}

Labeled Statement

In Java, you label a loop statement to make it the target of a break or continue statement. You can have one or more labels separated by a colon. The following example shows a label definition:

ReturnPoint: while ( x > 10 )
{
. . .
}

Any line can be labeled, but the compiler only recognizes labels at the beginning of loops or blocks, such as a while loop, when used as a target with the break statement. Also, label names are independent of all other kinds of names used in the program. For example, you can have a variable defined as an integer and, in the same block of code, also have a label with the same name. The compiler can distinguish between both entities.

The most common use of labels is with the break statement. The following example illustrates this usage:

int index = 1;
int index2 = 1;

outerLoop: while (true)
{
    index = index + 1;
    index2 = 1;
    innerLoop: while (true)
    {
        index2 = index2 + 1;
        if (index2 == 3)
        break outerLoop;
    }
}

First, two loops are defined, one outer and one nested inner. The outer loop is labeled outerLoop, and you are able to exit directly from that outer loop using a labeled break statement, as shown. If you did not specify the outerLoop parameter with break, then you would only have exited the innermost loop. Note that you are not jumping to a la- beled statement, but rather exiting from a labeled loop. This should be used sparingly, as some will argue it is not a good structured programming style. It is better, they say, to test a variable's value in the while loop expression and set the variable in the loop body to force exit. (The if, for, while, and break statements are covered in detail in the next chapter.)

The example in Listing 3.2 summarizes all of the Java statement types discussed so far. To get "extra credit" for this lesson, take a little pop quiz by considering each statement. Decide which one is syntactically correct and which one the Java compiler will flag as an error. We will step through the example and discuss each line.

Listing 3.2: A summary of Java statement types.
Start example
public class Statement
{
    public static void main(String args[])
    {
        int x = 8;
        for (int i=0;i<10;i++)
        { }
        label1:
        while(x<0)
        {
            break label1;
            ;
        }
        if (x == 10);
            ;
        {};
        {
        System.out.println("I am in a block!");
        }
        label0:;
        label2:label3:label4:;
    }
}
End example

The first structure is a for loop with all header information correctly specified. It in- cludes an empty block. This is syntactically correct in Java. Next is a label statement with a loop that breaks out of the label. Again, this is correct. Notice that on the line after break label1;, an empty statement is specified with nothing other than a semicolon. A correctly specified if statement comes next, with an empty body: if ( x==10 ); . Then, there is an empty block followed immediately by an empty statement. Both are valid in Java, and so is the block that includes the print statement. Finally, several labels are defined, separated with colons. This is also correct!

As it turns out, the Java program in Listing 3.2 compiles cleanly. It is not terribly useful, but it is valid. It demonstrates many of Java's syntax rules and statement types. Feel free to use it!


Expressions And Operators

Expressions in COBOL and Java are used to fetch, compute, and store values. We refer to those that compute and store values as computational expressions, and those that simply fetch a value without altering it as fetch expressions.

Table 3.6 illustrate the distinction between the two types of expressions. Assume that the loop expression must evaluate to true or false in both COBOL and Java.

Table 3.6: Fetch Expressions.
COBOL | Java
PERFORM UNTIL ITERS = 100 | while (iters < 100)
. . . | {
COMPUTE ITERS = ITERS + 1 | . . .
END-PERFORM | iters = iters + 1;
 | }

The COBOL expression returns true when the iters data item reaches 100, and ends the loop then. The Java expression returns true as long as the iters variables is under 100, and exits the loop when it doesn't and the expression returns false. Both of these are fetch expressions. They do not alter any variable in the expression.

Table 3.7 illustrates a computational expression. This expression computes the value iters + 1 and stores the result of the computation back into iters. Strictly speaking, the COBOL example is not an expression, only the right side of it is, according to the COBOL language definition.

Table 3.7: Computational Expressions.
COBOL | Java
COMPUTE ITERS = ITERS + 1. | iters = iters + 1;

In Java, everything up to the semicolon is considered an expression—the semicolon simply turns the expression into a statement. Unlike COBOL, that expression is also valid in other situations. For example, in Java it would be legal to code this:

X = (iters = iters + 1);

In this case, that whole expression of "iters = iters + 1" is simply used as a sub-expression in an assignment statement. Note that the nested assignment of that sub-expression is actually performed. So the side effect of this is not only to assign a value to X, but also to assign a new value to iters! This is not a good programming style, but alas, it is legal.

The following sections discuss computational expressions and their associated operators in both COBOL and Java, and provide more details on fetch expressions.

Computational Expressions

Operators are commonly classified by the number of values (operands or parameters) on which they operate. This distinction is useful because many operators have different (although related) meanings, depending on whether they are combined with two operands or only one.

For example, if you remember algebra from your school days, you know that you can use -i as the equivalent of 0-i, right? In the first case, the minus sign acts on the single numeric value, performing negation; in the second, it is operating on two numeric values. An operator that acts on a single operand is known as a unary operator. An operator that acts on two operands is known as a binary operator. In this example, the minus sign can be used as either a binary operator or a unary operator.

Arithmetic Operators

Table 3.8 lists all of the binary arithmetic operators used in both COBOL and Java.

Table 3.8: Binary Arithmetic Operators.
Java | COBOL Verb | COBOL Expression
+ (addition) | ADD | +
- (subtraction) | SUBTRACT | -
* (multiplication) | MULTIPLY | *
/ (division) | DIVIDE | /
% (modulus or remainder) | DIVIDE GIVING REMAINDER |
 |  | ** (exponent)

The binary arithmetic operators are +, -, *, and /, and the modulus operator, %. Java provides a modulus operator (%), but COBOL does not. On the other hand, COBOL provides an exponent operator (**) but Java does not.

COBOL, through its COMPUTE statement and arithmetic symbols in expressions (versus the wordier IS GREATER THAN type of alternatives), supports arithmetic expressions that very closely match Java. Consider the examples in Table 3.9, which show different expressions using various binary arithmetic operators in both lan- guages, but using COBOL's COMPUTE statement.

Table 3.9: Binary Operator Examples.
COBOL | Java
COMPUTE X = X + 1. | X = X + 1;
COMPUTE X = X / 2. | X = X / 2;
COMPUTE A = A + 2 * 3. | A = A + 2 * 3;
COMPUTE A = (A - 3) / 2. | A = (A - 3) / 2;

In the first example, the additive operator (+) adds the value contained in X to the numeric literal 1 and places the sum back in variable X. The +, -, *, and / arithmetic binary operators all operate similarly.

The next example uses the division operator to divide X by 2. The next two examples show expressions that use the add and multiply operators. In the first case, given that the precedence of multiplication and division are higher than subtraction and addition, the number 2 is multiplied by 3, the result is added to the variable A, and then the result is stored back in A.

In the last example, the parentheses force the subtraction to occur before the multiplication.

The binary operator (%), remainder, or modulus produces a pure value that is the remainder from an implied division of its operands. This is equivalent to using the REMAINDER phrase of the DIVIDE statement in COBOL.

Consider Table 3.10. The code in this example calculates the number of minutes and seconds, given a value in seconds. If X contains the value 185 seconds, the division operation produces a value of three minutes, and the remainder produces a value of five seconds.

Table 3.10: Remainder and Modulus.
COBOL | Java
DIVIDE X BY 60 | minutes = X / 60;
GIVING MINUTES | seconds = X % 60;
REMAINDER SECONDS. |

The only operator we have not yet discussed is the exponentiation (**) operator. This operator is used in COBOL, but not in Java. Instead, Java provides a method supplied in its Math class, covered later in this chapter, that supports this function.

Note that Java has only the assignment operator, which maps closest to COBOL's COMPUTE statement. There are no alternative operators like COBOL's ADD, DIVIDE, MULTIPLY, and SUBTRACT. Just as these are functionally equivalent to COMPUTE, Java does not suffer by having only assignment statements and expressions. As an aside, note that COBOL's COMPUTE does not allow for the remainder operation just shown, but take heart—it is now available through the intrinsic function REM (remainder), at least for OS/390, as in:

COMPUTE SECONDS = FUNCTION REM (X 60).

Assignment Operators

Once a variable has been declared, you can assign a value, another variable, or an expression to it using the assignment operator (=). In fact, you saw this simple form of assignment in the previous section. To assign the value zero to the variable X, you code as shown in Table 3.11.

Table 3.11: Assignment Example.
COBOL | Java
COMPUTE X = 0. | X = 0;

In COBOL, you can perform simple assignments using any of the SET, MOVE, INITIALIZE, or COMPUTE statements. Java has only the assignment operator for setting a variable to an arbitrary value, or to the value of an expression.

To reduce the number of assignment statements, Java allows the stringing of variables together. If you want to assign three variables to the same value, for example, you can string them together in a single statement, as follows:

A = B = C = 25;

In this example, all variables will contain the value 25. COBOL also has this capability, by specifying multiple target identifiers on its MOVE or COMPUTE statements, although the syntax is different. While in COBOL you simply specify multiple target data items, space delimited, in Java, you must put the assignment operator between each pair of target variables. In both languages the target must be a variable, not an expression or literal.

Java takes this concept one step further and introduces other related assignment statements, whose goal is to simply reduce keystrokes. With an expression such as

x = x + 1;

the language designers noticed that the variable on the left side of the assignment operator is repeated on the right side. They shortened this construct to

x += 1;

and produced a new set of variations for assignment statements. These variations are shown in Table 3.12. The combination of the + and = operators creates a new operator, the += operator.

Given that there are many other binary opera-tors in Java (covered shortly), the language designers introduced the new x= operator, where x stands for other supported binary operators. These include not only the arithmetic operators you have already seen, but also all binary operators: +, -, /, *, %, ^, &, |, <, >, and >>.

Table 3.12: Contracted Operators in Java.
Operator | Example | Meaning
= | A = A+B | assignment
+= | A += B | A = A + B
-= | A -= B | A = A — B
/= | A /= B | A = A / B
*= | A *= B | A = A * B
%= | A %= B | A = A % B
^= | A ^= B | A = A ^ B
&= | A &= B | A = A & B
|= | A |= B | A = A | B
<= | A <= B | A = A < B
>= | A >= B | A = A > B
>>= | A >>= B | A = A >> B

The general rule is as follows:

If expression1 and expression2 are expressions, then

expression1 x = expression2

is equivalent to

expression1 = expression1 x
expression2

where x stands for one of the operators mentioned. Obviously, both forms are allowed in Java (and in C and C++ as well). This format is a shorthand version that accomplishes the same thing with less typing. It hails originally from C, where the design philosophy was less is more.

As with all the other contractions you'll see in Java, the rule is "if you don't like it, don't use it." Some people think using these formats makes maintenance difficult for subsequent programmers, and are used by programmers as a form of personal triumph over others. However, they are pretty commonly used now, and you will find they do save some typing, so we don't personally have a concern with their use. Whatever gets you through the night. (That's when you code, isn't it?)

Incrementing, Decrementing, and Unary Operators

Java provides two unusual operators, also borrowed from C, for incrementing and decrementing variables. These are the pre/post increment/decrement operators. The increment operator (++) adds one to its operand, while the decrement operator (— —) subtracts one from its operand. These are most commonly used to increment or decrement the index of an array (a table, in COBOL) or other incrementing value inside a loop. The example in Table 3.13 illustrates a while loop that uses the ++ operator for incrementing its index.

Table 3.13: The Increment Operator.
COBOL | Java
COMPUTE IDX = 0 | idx = 0;
PERFORM UNTIL IDX > 10 | while (idx < 10)
******* some code | {
COMPUTE IDX = IDX + 1 | // some code
END-PERFORM | idx ++;
 | }

The primary difference between the example for each language is in the way the index data item or variable is incremented. In COBOL, a common way to do this is to use the COMPUTE or ADD statements and add one to the data item. If you are incrementing a table index, you use SET UP BY 1, but only for table indices.

Java does not have any unique syntax for a table index—a variable is a variable is a variable. In Java, using the traditional assignment statement with the + operator is valid, too, as in idx = idx + 1;. You must admit, however, that the contracted syntax idx++ does save a full five characters of typing in this case.

These operators are unusual in that both ++ and — — can be used as either prefix or suffix operators. Table 3.13 shows ++ used as a suffix operator; that is, it is placed after the operand. To use it as a prefix operator, you would code the expression as follows:

++idx;

What is the difference between these two forms? When ++ and — — are prefix operators, the operand will be incremented or decremented by one before its value is used in the expression. When used as suffix operators, the operand will be incremented or decremented by one after its value has been used in the expression.

Consider the following examples:

a = 5;
b = 5;
x = ++a + 50; // x is 56 after execution
y = 50 + b++; // y is 55 after execution

In the first case, the ++ operator prefixes the variable a. The operand a is incremented by one (changed to six) before it is used in the expression. This value, in turn, is added to 50, and the sum of 56 is placed in the variable x. In the second case, 50 is added to the content of operand b, which is five, and the sum of 55 is placed in the variable y. Only after that is b incremented. Note that x = ++a + 50; is equivalent to

a = a + 1;
x = a + 50;

and y = 50 + b++; is equivalent to

y = 50 + b;
b = b + 1;

Got it? This is a tricky thing to understand and to parse mentally when you see it! However, remember: If you don't like it, don't use it.

There is something else unique about these operators. Unlike all other arithmetic operators, they have side effects on their operands even when used in fetch expressions, such as the right side of an assignment statement. Beware of this!

In the preceding example, for instance, both a and b are affected by the statements. At the end, both have been incremented. The difference between prefix and suffix use is relative to the target of the assignment statement, but, as far as the variables a and b are concerned, both had the same effect of increasing their value by one. In our example, as part of the expression evaluation, the value of b is incremented to six; however, the increment occurs after the expression has been evaluated because the ++ operator is used as a suffix. These operators are similar to those in C and C++, with the exception that C and C++ do not allow the operand to be of type float. Notice also that these operators can only be applied to variable operands; they cannot be applied to expressions. For example, expressions like (a+b)++ or ++(a+b) are invalid. Both of these operators are considered unary operators because they are used with one argument or operand.

These Java operators are comparable to COBOL's ADD and SUBTRACT statements without the GIVING phrase. For example, ADD 1 TO A is very similar to ++A in Java. Both have a side effect on the variable A, which is to add one to the current contents. Similarly, SUBTRACT 1 FROM A is very similar to —A in Java. The only difference is that Java allows these expressions inside other expressions, and hence distinguishes between postfix and prefix notation. This distinction makes no sense in COBOL because you can- not assign the result to another data item anyway, unless you use the GIVING phrase, which changes the delta-add behavior.

Table 3.14: Unary Operators.
Operator | COBOL Operator | Java Operator
Increment | N/A | ++
Decrement | N/A | – –
Unary plus | + | +
Unary minus | - | -

Table 3.14 lists the COBOL and Java arithmetic operators. These operators take exactly one argument; that is, they are unary operators. The examples in Table 3.15 illustrate how to use the unary minus and plus operators in both COBOL and Java.

It's interesting to note that the unary plus operator really does nothing, while the unary negation operator actually negates the given operand. Thus, if y is set to -1, then -y evaluates to 1, while +y evaluates to -1. Mathematically, the + operator multiplies by +1, and the - operator multiplies by -1, in both languages.

Table 3.15: Unary Operator Examples.
COBOL | Java
COMPUTE X = -X. | x = -x;
COMPUTE X = +X. | x = +x;

Relational and Logical Operators

COBOL and Java have several operators for testing equality and magnitude. All relational expressions using these operators return a Boolean value of true or false. For example, to test if variable X is greater than ten, you can have an expression such as X > 10 in both languages. This expression is most useful when creating a block of code based on an equality test or a test for magnitude. For example, the same test expression X > 10 can be used on the if or any loop statement expression. Table 3.16 illustrates relational expressions used on an if statement.

The example tests to see if the PREFIX data item or prefix variable is equal to the character A. If it is, then the expression evaluates to true and the body of the IF or if structure is executed. Otherwise, control is transferred past the end of the IF block. Notice that the equality operator is = or EQUAL in COBOL, and == in Java. (The equal operator is only used as the assignment operator in Java.)

Table 3.16: Relation Expressions Example.
COBOL | Java
IF PREFIX = "A" THEN | if (prefix == 'A')
* some code | {
* more code | // code
END-IF | }

Expressions that result in Boolean values of true or false can be combined by using logi- cal operators to form more complex tests. For example, if you wanted to test for data item X being greater than 10 but less than 20, you could have a COBOL expression such as: (X>10) AND (X<20). This syntax combines both sub-expressions using the AND operator. The equivalent of the AND operator in Java is the && operator. As you will see, Java almost always uses doubled-up characters, such as == and &&, in its logical or Boolean bi-nary-expression operators. Ok? Ok?

Also, notice that when an expression needs to be enclosed in parentheses in Java, these combined expressions need to be enclosed in outer parentheses, as in

if ((x>10) && (x<20))

As expected, both COBOL and Java provide logical operators to combine relational tests. These operators in COBOL are AND, OR, and NOT, while Java uses the &&, ||, and ! operators. Java has an additional operator that is not supported in COBOL, namely the exclusive OR operator (^). In this case, both sides must not be the same; that is, one or the other, but not both, evaluate to true.

As mentioned, Java uses the && operator for its logical AND. In this case, the expression is true only if both expressions are true; if either expression is false, the entire expression is false. You can use the single & operator, as well. The difference between these two operators is in the internal expression evaluation. For the & operator, both sides of the expression are evaluated, regardless of the outcome. For the && operator, if the left side of the expression is false, the entire expression is obviously false, and the right side is never evaluated.

We recommend always using the doubled-up version, which is equivalent to COBOL's AND, as sometimes the right side is not valid if the left is true, for example:

((x != 0) & ((y / x) > 0))

In this case, if x is equal to zero (the left sub-expression is false), then you would be dividing y by zero in the right sub-expression.

Tables 3.17 and 3.18 summarize the relational and logical operators in both COBOL and Java.

Table 3.17: Relational Operators.
COBOL XX | COBOL Expression | Java | Description
<IS> EQUAL <TO> | = | == | Equal.
<IS> NOT EQUAL <TO> | NOT = | != | Not equal.
<IS> GREATER <THAN> | > | > | Greater than. Prefix with NOT or ! for negation.
<IS> LESS <THAN> | < | < | Less than. Prefix with NOT or ! for negation.
<IS> GREATER <THAN> OR | >= | >= | Greater than or equal to. EQUAL <TO>
<IS> LESS <THAN> OR EQUAL | <= | <= | Less than or equal to.
Table 3.18: Logical Operators.
COBOL | Java | Description
OR | || or | | Logical OR.
AND | && or & | Logical AND.
NOT | ! | Logical NOT, or negation.
 | ^ | Exclusive OR.

The logical OR operators (| and ||) are similar to the logical AND operators (& and &&). You can use either variation, | or ||. The OR expressions result in true if either or both operands evaluate to true. If both operands are false, the expression evaluates to false. As with & and &&, the | operator evaluates both sides of the expression, regardless of the outcome. With the || operator, if the expression on the left side is true, the expression is obviously true, and the right side is never evaluated. In general, the && and || operators are the most commonly used. As you will see in the following section on bitwise operators, the operators & , |, and ^ are used with bit manipulations, as well.

The negation operator (!) is a unary operator that negates the Boolean expression following it, as in if (!(x > 10)). If the expression operand evaluates to false, after negation, it evaluates to true. Compare this to the not-equals operator (!=), which is a binary operator comparing two operands, returning true if both have different values. The ! operator in Java is identical to the NOT operator in COBOL, but unlike in COBOL, you should always put the expression to be negated inside parentheses, unless it is a single variable of type Boolean. (This and all data types are covered in more detail in chapter 5. Data type is the Java term for category in COBOL.)

Note that for both languages it is very useful when using negation to know DeMorgan's Rule from the study of Boolean algebra. This rule describes how to negate a Boolean expression of any complexity, and can save you much work when trying to manually ripple a negation through an expression. The rule is, to negate an OR expression, negate each operand and change the OR to an AND. To negate an AND expression, negate each operand and change the AND to an OR. For example, this:

if ( ! (day != MONDAY) || (age <65) )

is the same as this:

if ( (day == MONDAY) && (age >= 65) )

likewise, this:

if ( ! ((day == MONDAY) && (age >= 65 )) )

is the same as this:

if ( (day != MONDAY) || (age < 65) )

Once again, note that white space is not important in Java. The spacing shown here is simply our preferred style; the code could as well have been written with no spacing inside the expressions.

Bitwise Operators

COBOL does not support bitwise operators. Java, on the other hand, has seven bitwise operators that allow you to perform operations on individual bits in integer values. All of these operators are inherited from the C and C++ languages, and, yes, you too can be a "bit" wiser, by reviewing Table 3.19.

Table 3.19: Java Bitwise Operators.
Bitwise Operator | Meaning
& | Bitwise AND.
| | Bitwise OR.
^ | Bitwise exclusive OR.
~ | Bitwise negation.
< < | Left shift.
> > | Right shift.
> > > | Zero-fill right shift.

It is very important to keep in mind that bitwise operators work only on integer types. Using any other type causes a compiler error. The bitwise AND operator (&) is often used to mask off some set of bits. It produces a value that is the bitwise and of its operands. Tra-versing each operand bit by bit, if the corresponding bit in both of the operands is one, the corresponding bit in the result variable is set to one. Otherwise, the corresponding bit in the result variable is set to zero. This is similar to the logical AND, where the resulting value is true if both operands evaluate to true, otherwise false. Listing 3.3 shows an example.

Listing 3.3: An example of using a bitwise AND operation.
Start example
public class Bit
{
    public static void main(String args[])
    {
        int firstNum  = 14;
        int secondNum = 12;
        int result;
        result = firstNum & secondNum;
        System.out.println("The result is : " +  result);
    }
}
End example

In this example, we perform a bitwise AND on two integer operands that contain the values 14 and 12. Knowing this, the internal representation of these two operand variables and of the resulting variable is shown here in bit format:

firstNum  == 14 == '00000000 00000000 00000000 00001110'
secondNum == 12 == '00000000 00000000 00000000 00001100'
result    == 12 == '00000000 00000000 00000000 00001100' (after &)

In this case, only the third and fourth bits have a one in both operands. That means that all of the values of the result variable will be zero, except these positions. This value evaluates to 22 + 23, which equals 12. The println method will print the value 12.

If you have followed the example so far, then the other bitwise operators will be just as easy to understand. For the bitwise OR operator, a one is returned in the result value at each bit position when either of the two operand values has a one in the same bit position; otherwise, a zero is returned. In contrast, the exclusive OR operator (^) returns a one at each bit position when the two values have opposite values in the same bit position. Listing 3.4 provides examples of these operators.

Listing 3.4: An example of bitwise OR operators.
Start example
public class OrxOr
{
    public static void main(String args[])
    {
        int firstNum  = 14;
        int secondNum = 12;
        int firstResult, secondResult;
        firstResult = (byte) (firstNum | secondNum);
        System.out.println("The first result is: " +  firstResult);
        secondResult = (byte) (firstNum ^ secondNum);
        System.out.println("The second result is: " +  secondResult);
    }
}
End example

In this example, the operand values 12 and 14 are used again. Here are the bit values of the operands and two results after doing a bitwise OR and a bitwise exclusive OR:

firstNum     == 14 == '00000000 00000000 00000000 00001110'
secondNum    == 12 == '00000000 00000000 00000000 00001100'
firstResult  == 14 == '00000000 00000000 00000000 00001110'
secondResult ==  2 == '00000000 00000000 00000000 00000010'

All this talk about twiddling bits might have you wondering how to print out the binary version of an integer variable. This is jumping ahead a bit, but there are classes in Java that mimic each of the built-in data types and that supply some cool methods. The class to use here is Integer, and the method is the static method toBinaryString, which takes an integer as input and outputs a string containing its binary or bitwise value. You might want to create your own static method called printBits in your own helper class that, in turn, uses this method in the Integer class:

public static void printBits(String name, int x)
{
    System.out.println(name + " == " + x + " == " +
                      Integer.toBinaryString(x));
}

Calling this with the firstNum and secondNum variables used in the examples so far gives the following output (note that the leading-zero value bits are not printed):

firstNum == 14 == 1110
secondNum == 12 == 1100

Finally, there is the inverse operator (~). It is similar to the logical negation operator (!), but at the bit level. That is, it negates whatever the value is in each bit of the operand, so ones become zeros, and zeros become ones. Imagine, for example, that the previous examples changed to this:

firstNum = ~firstNum;
secondNum = ~secondNum;

You would get the following before and after results:

firstNum  == '00000000 00000000 00000000 00001110' (before)
secondNum == '00000000 00000000 00000000 00001100' (before)
firstNum  == '11111111 11111111 11111111 11110001' (after ~)
secondNum == '11111111 11111111 11111111 11110011' (after ~)

Shift Operators

The shift operators complete this discussion of bitwise operators. As you work with the bits that make up an integer number, you might need to shift the bit values to the right or to the left. In Java, this operation is performed with the left bitwise shift operator (<<) and the right bitwise shift operator (>>). Listing 3.5 illustrates how these operators work.

Listing 3.5: Using shift operators.
Start example
public class Shift
{
    public static void main(String args[])
    {
        int firstNum = 2;
        int firstResult, secondResult;
        firstResult = (firstNum < 2);
        System.out.println("The first result is : " + firstResult);
        secondResult = (firstResult > 1);
        System.out.println("The second result is : " +  secondResult);
    }
}
End example

This example starts by initializing the variable firstNum to two. This is represented internally by the following sequence of bits shown for firstNum:

firstNum     == 2 == '00000000 00000000 00000000 00000010'
firstResult  == 8 == '00000000 00000000 00000000 00001000'
secondResult == 4 == '00000000 00000000 00000000 00000100'

The first operation performed on the variable is shifting left twice. Then, we use the result from the first operation to shift right once. As a result, the internal representation for each of the variables firstResult and secondResult is as shown. Notice that shifting left is equivalent to multiplying by the corresponding power of two, and shifting right is equivalent to dividing by the corresponding power of two, with possible truncation in both cases. These operations are generally more efficient than using multiplication or division.

Unlike the inconsistencies found in some implementations of C and C++, Java always preserves the sign bit (the leftmost bit) after performing the right bitwise shift. However, Java also offers another bitwise operator, the shift right operator (>>>), that does not preserve the leftmost sign bit. In other words, the operator always sets the leftmost bit to zero.

Conditional Operators

The conditional operator (? :), also called a ternary operator, also hails from C. COBOL does not have it. This operator is used to choose alternate values for a variable, based on an expression that evaluates to true or false.

The following example shows how to use this operator and compares its use to the if statement:

result = (idx == 20) ? 30 : 35;
// same as...
if (idx == 20)
    result = 30;
else
    result = 35;

The Boolean expression before the question mark is evaluated first. If it is true, then the expression between the question mark and the colon is evaluated, and its value is assigned to the variable on the left (result, in this example). Otherwise, the expression between the colon and semicolon is evaluated, and its value is assigned to the left variable.

Conditional expressions are only suitable when you want to return one of two values, depending on the value of a Boolean expression. Conditional expressions are compact, saving you keystrokes, but they are a headache to read. Again, if you don't like it, don't use it.

Operator Precedence

Table 3.20: COBOL Operator Precedence.

( )

unary +, unary -, NOT

Intrinsic function calls

**

*, /

Binary + and binary – (addition and subtraction)

=,<> , >, >=, <, <=

AND

OR

At this point, you have seen all of the Java and COBOL operators that you can use in expressions. In this section, we discuss operator precedence: the order of evaluation of multiple operators in expressions. These rules govern which operator is evaluated first, second, third, and so on. Operator precedence is important in any programming language; it determines the final result of an expression, which depends on the order in which operands are evaluated. Table 3.20 summarizes operator precedence in COBOL.

In an earlier program example, you saw the expression A + 2 * 3. If the value of A is four, then what would the result be after evaluation—18 or 10? If you add first, the final value will be 4 + 2, or 6, multiplied by 3, which gives the result 18. If you multiply first, the value will be 2 * 3, or 6, with 4 added, which gives the result 10. This is why operator precedence has to be established.

As you can see in Table 3.20, the multiplication operator has higher precedence, so the value you would get from the example expression is ten. Multiplication will be done first, followed by addition. This is the logic that the compiler uses to establish the evaluation of any given expression in a language.

Table 3.21: Java Operator Precedence.
Operators | Associativity
() | L
++, – –, ~, !, -, (type cast) | R
*, /, % | L
+, - | L
+ (concat) | L
>>> | L
>>, << | L
<, <=, >, >= | L
instanceof | L
= =, != | L
& | L
^ | L
&& | | L
| | | L
?: | R
=, *=, /=, %=, +=, -=, <<=, >>=, >>>=, &=, ^=, |= | R

High-precedence operators are evaluated before low-precedence operators. What happens if you have operators with the same precedence? For example, what would the expression (X / 4 * 3) evaluate to? If X is equal to 12, then will the result be 1 or 9? The result depends on how the compiler parses and evaluates the line for operators of the same precedence. Is parsing done from left to right, or right to left? For most operators, both COBOL and Java parse and evalu-ate from left to right. This means that the preceding expression in both languages evaluates to nine. Table 3.21 shows operator pre-cedence in Java.

Again, operators in the top of the table have higher precedence than those appearing below them. For operators that have equal pre-cedence, the Associativity column indicates which ones will be evaluated first: L for left-to-right and R for right-to-left. See chapter 5 for a discussion of "type casting," chapter 7 for information on "string concate-nation," and chapter 9 for a description of "instanceof."

Fetch Expressions

As mentioned earlier in this chapter, fetch expressions are the most elementary type of expression. Their primary use is to construct, or fetch, values without performing any permanent computation on them. For example, accessing a variable in a class and assigning it to another variable, or executing a call to a method that returns a value, are examples of fetch expressions. The following three sections discuss the different kinds of fetch expressions.

Null Expressions

The fetch expression null produces a special object reference value that does not refer to any object, but is assignment-compatible with all object reference types. An object reference type is simply any variable declared to be of a class type. the default value for these types of variables is null before they have been assigned to an object by use of the new operator. If you assign an object reference variable to null, the object it was pointing to will be left "dangling" if no other variables point to it, and it will be swept up by the garbage collector.

The special value null is most easily thought of as a language-supplied constant. Actually, it is an expression—a fetch expression. This is splitting hairs, though, and not of much consequence. (Note that null is not considered a zero, as it is in C and C++.)

If you need to determine whether an object reference variable has yet to be assigned to an object, you can compare it to null, as in the following:

if (myObject == null) // not instantiated yet?
myObject = new MyClass(); // instantiate it now

Method Call and Array Index Expressions

A method call expression is a primary expression that invokes a method. This is similar to a program call or an intrinsic function call in COBOL. A method call expression fetches a value returned by the method. Similarly, an array or table index expression produces an array element when it is evaluated. (Arrays are discussed in chapter 6).

The following illustrates both of these expressions:

myVariable = myObject.myMethod(); // call method myMethod in myObject
y = myEmployeeArray[i]; // retrieve i'th entry of array myEmployeeArray


Other Mathematical Functions

Earlier in this chapter, we introduced the basic math operators that are used in expressions. These include +, -, *, /, and %. COBOL and Java support many other mathematical functions, such as the intrinsic functions in COBOL and the methods in supplied classes in Java. The Java-supplied class Math, for example, contains methods for performing basic numeric operations, such as exponentiation, logarithms, square roots, and the trigonometric functions.

To help ensure portability of Java programs, the definitions of many of the numeric functions in the Math class are implemented using certain accepted, published algorithms. These algorithms, which were originally written in C, are available from the network library netlib, as the package Freely Distributable Math Library (FDLIBM). They were rewritten in Java and supplied as the Java Math class. The Java math library is defined with respect to the version of FDLIBM dated January 4, 1995.

Consider an example of the COBOL intrinsic function SQRT (square root):

COMPUTE X = FUNCTION SQRT (25).

This puts the value 5 in the numeric data item X.

The same result can be accomplished in Java using the Math class, which is found in the java.lang package. This package is always imported for you. Listing 3.6 illustrates the same example written in Java, but with enough context to be completely compilable.

Listing 3.6: Using the Math class in Java.
Start example
public class ABS
{
    public static void main(String args[])
    {
        double x; x = Math.sqrt(25);
        System.out.println("The square root is: " + x);
    }
}
End example

As in the COBOL example, this gives 5. (Actually, it gives 5.0, since a floating-point data type is returned. Data types are covered in chapter 5.) This is just one example of the many different methods available in the Math class.

Because this method, like all the methods in the Math class, is defined as static, you do not have to instantiate the Math class first with the new operator. All these functions are standalone, and take what they need as parameters.

We have shown you only a few examples of the methods available in the Java Math class. There are many more that you can use, including those listed in Table 3.22.

Table 3.22: Methods in Java's Math Class.
Method | Description
abs(double) | Returns the absolute value of a double value.
abs(float) | Returns the absolute value of a float value.
abs(int) | Returns the absolute value of an int value.
abs(long) | Returns the absolute value of a long value.
acos(double) | Returns the arc cosine of an angle, in the range of 0.0 through pi.
asin(double) | Returns the arc sine of an angle, in the range of -pi/2 through pi/2. Note that there is a constant named pi defined in the Math class for your convenience.
atan(double) | Returns the arc tangent of an angle, in the range of -pi/2 through pi/2.
atan2(double,double) | Rectangular coordinates (b, a) to polar (r, theta).
ceil(double) | Returns the smallest (closest to negative infinity) double value that is not less than the argument and is equal to a mathematical integer.
cos(double) | Returns the trigonometric cosine of an angle.
exp(double) | Returns the exponential number (e.g., 2.718) raised to the power of a double value.
floor(double) | Returns the largest (closest to positive infinity) double value that is not greater than the argument and returns the greater of two double values.
max(float, float) | Returns the greater of two float values.
max(int, int) | Returns the greater of two int values.
max(long, long) | Returns the greater of two long values.
min(double, double) | Returns the smaller of two double values.
min(float, float) | Returns the smaller of two float values.
min(int, int) | Returns the smaller of two int values.
min(long, long) | Returns the smaller of two long values.
pow(double, double) | Returns the value of the first argument raised to the power of the second argument.
random() | Returns a random number between 0.0 and 1.0.
rint(double) | Returns the closest integer to the argument.
round(double) | Returns the closest long to the argument.
round(float) | Returns the closest int to the argument.
sin(double) | Returns the trigonometric sine of an angle.
sqrt(double) | Returns the square root of a double value.
tan(double) | Returns the trigonometric tangent of an angle.

Finally, when we discussed arithmetic operators earlier in this chapter, we mentioned that COBOL has an exponentiation operator, whereas Java does not. However, the pow method in the Math class called does this function for you. As an exercise, you might want to experiment with it.


Summary

This chapter introduces you to the following concepts:

  • There are three kinds of comments in Java: multiline, single-line, and JavaDoc.

  • The javadoc command is a tool supplied in the JDK to help you document your classes and methods. It converts the comments in a Java source file into an HTML document that may read by any Web browser.

  • In Java, variable name length is unlimited.

  • There are two kinds of expressions in Java: computational and fetch. Computational expressions use operators and can cause side effects; fetch expressions are used to fetch a value, but do not do any computation on them.

  • Java's assignment operator is the single equals sign (=).

  • Java has numerous math operators: +, -, /, *, %, ++, – –.

  • Java has numerous relational operators: = =, !=, <, >, <=, >=.

  • Java has numerous Boolean operators: &&, ||, !.

  • Java has seven bitwise operators: &, |, ^, ~, <<, >>, >>>.

  • Java has a ternary or conditional operator, ? :.

  • Java allows the contraction of binary operands, as in x += 10;.

  • Java has a whole class (java.lang.Math) in the JDK that has methods to cover a variety of math functions, including sin, cos, tan, abs, and sqrt.


Chapter 4: Structured Operations and Statements

Overview

Control structures form the smallest executable unit in COBOL and Java programs. They control the order in which statements will be acted upon by the computer. Control structures can be divided into four basic categories:

  • Sequence structures, in both COBOL and Java, are a sequence of imperative statements allowing control to flow from one statement to the next in the order in which they were written in the program. That is, flow is sequential. Examples in COBOL are the MOVE and SET statements. An example in Java is the assignment statement. These statements execute consecutively. They do not cause the flow of control to shift. When control comes to the end of a program, it usually terminates. These statements are covered in chapter 3.

  • Decision structures, or conditional statements, allow control to flow in a different path, depending on the result of a condition. You can conditionally select alternate paths for your program. Decision statements include the following:

    • IF in both COBOL and Java.

    • EVALUATE in COBOL, switch in Java.

    • SEARCH…WHEN in COBOL.

    • Imperative statements in COBOL such as ON SIZE ERROR, ON OVERFLOW, ON EXCEPTION, AT END, and INVALID KEY.

  • Loop structures allow control to flow iteratively for a specified number of times, or until a condition is met. Loop structures include the following:

    • PERFORM…VARYING in COBOL, for in Java.

    • PERFORM…UNTIL in COBOL, while in Java.

    • PERFORM…WITH TEST AFTER…UNTIL in COBOL, do-while in Java.

    • PERFORM…TIMES in COBOL.

  • Transfer structures allow control to flow to another part of the program. Transfer control structures include the following:

    • GO TO in COBOL.

    • break/continue in Java.

    • PERFORM in COBOL for calling out-of-line paragraphs.

    • CALL in COBOL, method call in Java.

    • GOBACK and EXIT PROGRAM in COBOL, return in Java.

    • STOP RUN in COBOL, System.exit(0) in Java.

    • USE in COBOL, try/catch and throw in Java.

Table 4.1 shows structured statements that are available in COBOL and their equivalent Java statements. Some less-common statements are not shown because they are not available in both languages.

Table 4.1: Structured Statements in COBOL Versus Java.

COBOL

Java

Description

IF expression THEN
END-IF

if (expression)
{
// statements;
}

Enables you to execute a code fragment based on a Boolean test.

EVALUATE expression
WHEN value
WHEN OTHER
END-EVALUATE

switch (test)
{
case value:
break;
default:
}

Enables you to execute a particular block of code among multiple choices, based on comparing an expression to one or more possible values.

PERFORM VARYING
FROM… BY …
UNTIL expression
statements
END-PERFORM

for (init;
expression;
increment)
{
// statements;
}

Loop a specific number of times based on an initial value, expression, and incre-ment. Loop while the expres-sion is true.

PERFORM
UNTIL expression
statements
END-PERFORM

while (expression)
{
// statements;
}

Loop while the expression is true.

PERFORM
WITH TEST AFTER
UNTIL expression
statements
END-PERFORM

do
{
// statements
}
while (expression);

Loop until the expression is false. Body is executed at least once.

GO TO

Java disallows this structure., while COBOL allows it. Our advice: do not use it!

break;
continue;

break transfers control out of the innermost structure, while continue ends the current iteration of the loop, and starts the next.

EXIT PROGRAM or GOBACK

return

Return to caller of method / program.

STOP RUN

System.exit(0);

End this run unit.

USE statement or ON
EXCEPTION, AT END or
INVALID KEY phrase

try/catch

Catch all program and file exceptions. See chapter 10 for more information.

Using CEESGL to signal a severity 4 condition or send-ing escape message (OS/400)

throw

Causes an exception to be thrown. See chapter 10 for more information.


Alternate Paths: Decision Structures

Let's turn our focus to the COBOL and Java statements that can conditionally alter the flow of control, such as IF/if and EVALUATE/switch.

If, Then What? Else, of Course

The if conditional statement enables you to execute different parts or blocks of code based on a simple test. The if statement in Java is nearly identical to the IF statement in COBOL. They are written differently, but they have the same function. Table 4.2 illustrates the two forms of the COBOL IF statement.

Table 4.2: COBOL's IF Statement.
COBOL Word Form | COBOL Relational Operator Form
IF AGE GREATER THAN 64 AND CURRDATE EQUAL TO MON THEN COMPUTE PRICE = 10 ELSE COMPUTE PRICE = 20 END-IF | IF AGE > 64 AND CURRDATE = MON THEN COMPUTE PRICE = 10 ELSE COMPUTE PRICE = 20 END-IF

The second column illustrates the less-wordy relational operator format of the IF expression in COBOL. In this format, you use the operators <, >, =, <=, and >= in place of Eng-lish words. The expression in this example forms two conditions: AGE must be greater than 64, and the day must be equal to MON (assume this is an initialized data item). If both conditions are true, the body of the IF statement is executed. If any one of these conditions is false, the body of the ELSE statement will be executed. The Java if statement, as you might have guessed from earlier examples, is closer to the second version of the COBOL IF statement.

The following illustrates the simplest form of the if statement in Java:

if (age>64 && currdate==MON)
   {
      price = 10;
   }
else
   {
      price = 20;
   }
Note

Remember that the conditional expression must be inside parentheses, no matter how simple.

As with COBOL, the expression is also often coded with parentheses around the sub-expressions or conditions, as shown in Table 4.3.

Table 4.3: Using Parentheses around Sub-expressions.

COBOL

Java

IF ((AGE > 64) AND
(CURRDATE = MON)) THEN
COMPUTE PRICE = 10
ELSE
COMPUTE PRICE = 20
END-IF

if ((age > 64) &&
(currdate == MON))
{
price = 10;
}
else
{
price = 20;
}

Because the && operator has precedence over the other operators, the use of parentheses here is simply a style preference that aids readability. The braces around the body are also optional if only a single statement exists after the if :

if ((age > 64) && (currdate == MON))
   price = 10;
else
   price = 20;

A problem will result if you add a second line later, but forget to add the braces, as in:

if ((age > 64) && (currdate == MON))
   price = 10;
   discount = 0.50;

Although this will compile, you will get an unexpected outcome. The second statement will be executed regardless of the outcome of the if test because it is not part of the body. If the body has more than one statement, you must enclose the block with braces. To prevent this type of error, always use braces, even for single statement blocks. Using braces for the body is similar to using COBOL's END-IF scope terminator.

The official Java syntax for the if statement is as follows:

if (expression)
   single-statement or block-of-statements
else
   single-statement or block-of-statements

There is no optional THEN term as there is in COBOL. The else part is optional. Single statements end in semicolons, while blocks are surrounded by braces.

Both languages allow you to specify an arbitrarily complex expression. If the expression evaluates to true, the following block of statements within the structure are executed. Java only allows Boolean expressions that ultimately evaluate to true or false. If you are familiar with C or C++, be aware that Java does not support the idea of allowing numeric expressions and mapping zero to false and any other number to true. Although Java syntax is, for the most part, identical to C's, this is one exception.

Java does not have any equivalent syntax mapping to COBOL's conditional expressions for CLASS CONDITION, condition-name, sign, or switch status testing. However, the same functions can be accomplished using standard if expressions as described here.

An interesting situation arises when you have more than two conditions. Consider an example in which you are testing for an age. You would like to have conditions that perform one set of instructions if the person is at least 65 years old, a second set if the person is less than 65 but not younger than 12, a third set if the person is less than 12 but not younger than 2, and a default set if the person is 2 or less. That adds up to a total of three different conditions (although there could easily be more), and a default condition that is processed if all three conditions are false. Both languages support this kind of testing by allowing you to nest the IF statements. The following illustrates the nesting feature in COBOL:

* Assign Ticket Prices for the Zoo based on age
  IF (AGE >= 65) THEN
    COMPUTE TICKET = 10
  ELSE
    IF (AGE > 12) THEN
      COMPUTE TICKET = 20
    ELSE
      IF (AGE > 2) THEN
        COMPUTE TICKET = 5
      ELSE
        COMPUTE TICKET = 0.

This situation represents the IF statement in its simplest form. All you have to do is add another IF statement after the ELSE statement with the new condition you want to test. For more complex conditions, additional ELSE IF structures can be added. What follows is the previous example written in Java:

// Assign ticket prices for the zoo based on age
if (age >= 65)
  ticket = 10;
else if (age > 12)
  ticket = 20;
else if (age > 2)
  ticket = 5;
else
  ticket = 0;

Notice, as is commonly seen in Java, that the else and if statements are lumped together on the same line for compactness. Remember: an else always matches the most recent if. Sound reasonable? You can easily get in trouble by using if statements as the body of the if statement itself. For example, consider the following:

if (Age < 65)
  if (Age > 12)
    Ticket = 20;
else
  Ticket = 10;

It might look as if the else matches the outerif, but it does not. Instead, it matches the inner if. You are better off using COBOL's style of indentation, but keep in mind you will see the compact version in other people's Java code.

You can avoid the situation altogether by writing these kind of if-if statements as single if statements with compound expressions, as shown here:

if ((Age < 65) && (Age > 12))
Ticket = 20;
else
Ticket = 10;

Too Many IFs? Time to Switch! One of the most common programming methods in any language is to test a variable for many possible values. You can use the IF/if statement to solve this kind of problem, but both Java and COBOL provide you with a more elegant solution. Consider the following code (assume MON through FRI are previously defined integer constants):

if (day == MON)
  {
    // do something
  }
else if (day == TUE)
  {
    // do something
  }
else if (day == WED)
  {
    // do something
  }
else if (day == THUR)
  {
    // do something
  }
else if (day == FRI)
  {
    // do something
  }
else
  {
    // default code
  }

This if statement determines the day of the week. The code in this Java example is very long, and you can imagine it would be just as long in COBOL with five if and four nested else statements. These nested statements can often be syntactically correct and yet not express the intended logic of the programmer. Incorrect else-if matching bugs are hard to find and modifications to the statements are risky to make.

As an alternative to IF/if statements, COBOL and Java offer the EVALUATE statement and the switch statement, respectively. Table 4.4 shows an example of each.

Table 4.4: COBOL's EVALUATE Versus Java's switch Statement.

COBOL

Java

EVALUATE CURRDAY
WHEN 1
COMPUTE PRICE = 11
WHEN 2
COMPUTE PRICE = 12
WHEN 3
COMPUTE PRICE = 13
WHEN OTHER
COMPUTE PRICE = 20
END-EVALUATE

switch (day)
{
case MON: // MON is a constant
price = 11;
break;
case TUE: // TUE is a constant
price = 12;
break;
case WED: // WED is a constant
price = 13;
break;
default:
price = 20;
} // end switch statement

The first important point to note is the improved readability of the EVALUATE/switch structure compared to nested IF/if statements. Also, if you look at the previous example, you can almost map the structures one-to-one between the languages. The switch statement maps to the EVALUATE verb, and the case statement maps to the WHEN phrase.

In Java, you have to use the break statement to indicate the end of the case statement body, unlike COBOL. The default statement in Java maps to the WHEN OTHER operation code in COBOL. Even the end brace } that ends the switch statement in Java maps to the END-EVALUATE terminator in COBOL.

Table 4.5 summarizes these mappings.

Table 4.5: COBOL's EVALUATE Versus Java's switch Sub-statements.
COBOL | Java
EVALUATE | switch
WHEN | case
WHEN OTHER | default
END-EVALUATE | } (end brace)

The COBOL EVALUATE statement and Java's switch statement seem to be very similar. However, they differ in the expression that is tested:

  • In the Java switch statement, the expression is only specified once, as part of the switch (expression) syntax. The result of the expression is then compared to each of the case statement values until a match is found, and that case body is executed. If no match is found, the default body is executed, if it exists. Otherwise, the switch statement completes without doing anything. The expression must resolve to a value of type (category) byte, character, short, or integer. The smaller types are promoted to integer by the compiler. (Types and promotion are discussed in chapter 5.) The case statements can only test for equality with the expression value. The first case value to match is the winner.

  • COBOL's EVALUATE statement is superior in function. It has many variations, and only the simplest matches Java's switch capabilities. For example, in COBOL, you can just use TRUE for the EVALUATE expression. The WHEN phrase allows you to specify any valid expression you like, and each WHEN can specify a different expression. COBOL simply selects the first WHEN that evaluates to true. This is a common use of the EVALUATE statement in COBOL, but it is not permitted in Java. Java only allows an integer expression or variable on the switch statement, and looks for matching integer values in the case statements. It does not even allow the equivalent of COBOL's THROUGH or THRU to test if the expression is within a given range.

There is an additional important difference. Did you notice that the COBOL EVALUATE statement was more compact than the Java switch statement? This is primarily because of the Java requirement to end each case statement block with a break statement. COBOL, on the other hand, implicitly defines the end of a WHEN phrase when it sees another one or the END-EVALUATE statement. This use of break makes Java more verbose than COBOL.

There are more sinister implications, however, than the size of the code. What do you think will happen if you forget to insert the break statement in Java to terminate the case clause? Rather than implicitly ending the block of code at the next case statement, Java will continue to execute all statements until it finally finds a break statement or the end of the switch statement. This can lead to some nasty bugs that are quite time-consuming to track down. This is actually by design. It allows you to group together cases that have common code. For example, suppose you want to execute one block of code for MON, TUE, and WED, and another one for THUR and FRI (assuming these are all constants of type integer). Here is how you exploit that break-less behavior to do it:

switch (day)
{ case MON:
    case TUE:
    case WED:
        // first block of code
        break;
    case THUR:
    case FRI:
        // second block of code
        break;
    default:
} // end switch statement

You have to do this grouping of case statements because Java, unlike COBOL, has no other case syntax for matching a range of values.

Note the syntax of the switch statement: the switch expression must be inside parentheses, there must be a brace after that expression and another at the end of the entire switch statement. Some people put that first brace at the end of the switch line, others (like us) prefer to put it on its own line immediately under the s in switch. There is no semicolon after that final brace. This is true for all brace-delimited blocks.

The case statements require one or more blanks after the term case, and the integer variable, constant, or expression, that follows must end with a colon (with or without leading blanks). In general, blanks and line breaks are totally irrelevant to the compiler, so you could code the entire thing on one single line if you chose. Typically, case statements are indented under the switch line, and the body of code for each case is further indented under that case statement.

One interesting point about case statements is that you can put a block inside them if you want to declare a variable local to the case logic. Remember, brace-terminated blocks are valid statements themselves, and so can be used anywhere a statement is allowed, for example:

switch (province) // province is an integer variable
{
   case ONTARIO: // ONTARIO is a constant integer
      { // new block so we can declare local variables
         float tax = 0.15;
         price = price * tax;
      }
      break;
   case . . . // and so on
} // end switch

In summary, then, Java's switch statement offers only a subset of the functionality of COBOL's EVALUATE statement, and specifically is for executing one of numerous break-terminated blocks of code depending on the value of an integer variable or expression. To map any of the other capabilities of COBOL's EVALUATE statement, you must revert back to complex if statements.

The Conditional Operator, ?:

Chapter 3 introduces the ternary (?:) conditional operator. Under certain circumstances, you can use it as a shorthand version of the if statement to indicate alternate paths through the code. However, if you are more comfortable using the if statement, then just use it; the results will be the same.

The following example illustrates an if statement that does a simple test for age less than two. If the test is true, a Boolean variable (discussed in chapter 5) is set to true; otherwise, the variable is set to false:

boolean freeTicket;

if (age < 2)
   freeTicket = true; // true is a keyword in Java
else
 freeTicket = false; // false is a keyword in Java

// ternary equivalent
freeTicket = (age < 2) ? true : false;

You can create a similar construct in COBOL, if you like. Use the EVALUATE statement with TRUE for the condition, for example:

01 IS-TICKET-FREE PIC 1 VALUE B"0".

   88 FREE-TICKET    VALUE B"1".
   88 NOTFREE-TICKET VALUE B"0".
. . .
EVALUATE TRUE
   WHEN AGE < 2
      SET FREE-TICKET TO TRUE
   WHEN OTHER
      SET NOTFREE-TICKET TO TRUE
END-EVALUATE


Loop Structures-They Get Around

 Team-Fly

Loop Structures—They Get Around

The three most common loop structures available in all modern languages are also available in Java and COBOL: the for loop (PERFORM…VARYING in COBOL), the while loop (PERFORM…UNTIL in COBOL), and the do…while loop (PERFORM…UNTIL…WITH TEST AT END in COBOL). Table 4.6 summaries the different loops that are available in both languages. The next section describes all three loops.

Table 4.6: COBOL In-Line Perform Versus Java Loop Statements.

COBOL

JAVA

PERFORM VARYING identifier-1
FROM identifier-2 / integer-1
BY identifier-3 / integer-2
UNTIL condition-1
body
END-PERFORM

for (initialization; condition;
increments)
{
// body
}

PERFORM UNTIL condition-1
body
END-PERFORM

while (expression)
{
// body
}

PERFORM WITH TEST AFTER UNTIL
body
END-PERFORM

do
{
// body
} while (expression);

The Java column of the table shows the constructs for the three types of loops (loop with index, loop while true, loop until false). In all cases, the body is inside braces or a block.

As with the if statement, these braces are optional if the body contains only a single statement.

The COBOL column of the table shows in-line PERFORM statements. Of course, the out-of-line variation, where a paragraph name is specified after the PERFORM verb instead of an in-line body of code, is also valid for these analogies. However, Java has no direct equivalent syntax. Rather, in Java, you simply replace the brace-delimited body with a single method call. Because it is only one statement, you can code it without braces, as shown in Table 4.7.

Table 4.7: COBOL Out-of-Line PERFORM Versus Java FOR, WHILE, and DO-WHILE.

COBOL

JAVA

PERFORM paragraph-1 VARYING
identifier-1
FROM identifier-2 / integer-1
BY identifier-3 / integer-2
UNTIL condition-1
END-PERFORM

for (initialization; condition;
increments)
object1.method1();

PERFORM paragraph-1
UNTIL condition-1
END-PERFORM

while (expression)
object1.method1();

PERFORM paragraph-1 WITH TEST AFTER

UNTIL condition-1
ND-PERFORM

do
object1.method1();
while (expression);

The following sections go through each of these three loop types in more detail.

Going "for" a Loop: The for Statement

The PERFORM VARYING loop in COBOL and the for loop in Java are used when a known, definitive number of iterations are required to execute a block of statements or operation codes. This kind of loop structure is called a determinant loop. A determinant loop has a starting and ending value and an index that controls iteration steps for the loop. You can also use the PERFORM…TIMES statement in COBOL for determinant loops, but there is no equivalent to this in Java, so we won't discuss it here. Really, this is just a simplified case of a PERFORM VARYING or for loop anyway.

Let's look at an example in both COBOL and Java. Consider a fully compilable and runable program that takes an integer value as input, and computes and then displays the factorial of that number. For example, the factorial of 3 is 3*2*1=6, and the factorial of 4 is 4*3*2*1=24. Listing 4.1 shows how you would do this in COBOL.

Listing 4.1: A COBOL example for computing and displaying the factorial of an integer.
Start example
IDENTIFICATION DIVISION.
   PROGRAM-ID.     TSTPRFRM.
   AUTHOR.         Phil Coulthard.
   ******************************************************
   * Test the PERFORM…VARYING statement                 *
   ******************************************************
   DATA DIVISION.
   WORKING-STORAGE SECTION.
      01 INDEX1  PIC 999 VALUE ZEROS.
      01 TOTAL   PIC 99999 VALUE 1.

   LINKAGE SECTION.
      01 INPUT-VALUE PIC 99.

   PROCEDURE DIVISION USING INPUT-VALUE.

   000-MAIN.
      PERFORM VARYING INDEX1 FROM 1 BY 1
            UNTIL INDEX1 > INPUT-VALUE
         COMPUTE TOTAL = INDEX1 * TOTAL
      END-PERFORM
      DISPLAY TOTAL.
      STOP RUN.
END PROGRAM TSTPRFRM.
End example

An integer parameter (such as 3) is taken in data item INPUT-VALUE, its factorial is computed in the PERFORM VARYING loop, and DISPLAY shows the result. The factorial is computed by multiplying the current index number INDEX1 by the current value of TOTAL (from the previous iteration of the loop) and making the result the current value of TOTAL. To prove that this works, you can code it, compile it, call it with values 3 and 4, and verify that the results shown are 6 and 24, respectively.

Now consider how you would code the same little program in Java, with the for statement, as shown in Listing 4.2.

Listing 4.2: A Java example for computing and displaying the factorial of an integer.
Start example
public class TestForLoop
{
   public static void main(String args[])
   {
      if (args.length == 0)
         System.out.println("You must pass a parameter");
      else
         {
            try
            {
               int inputValue = 0;
               int total = 1;
               int index1 = 1;
               inputValue = Integer.parseInt(args[0]);
               for (index1=1; index1 <= inputValue; index1++)
                  total = index1 * total;
               System.out.println(total);
            }
            catch (NumberFormatException exc)
            {
               System.out.println("Sorry, not a valid number");
            }
         }
      } //end main method
   } //
end of class TestForLoop
End example

The example in Listing 4.2 is a little complex because Java only takes strings as command-line parameters, so you have to convert the parameter to an integer. Recall that the main method gets control from the command line, and the command-line parameters are mapped to the String array parameter, usually defined with name args. The code has to check whether it has been passed at least one parameter, and issue a message if it has not.

The string-to-integer conversion of the first parameter uses the static method parseInt in the Java-supplied Integer class (in the java.lang package, which is always imported for you). Because this static method can throw an exception if the input string is not valid, you have to put it inside a try block and code a catch block to handle that exception. (Exceptions, and try and catch blocks, are described in chapter 10.)

The real meat of Listing 4.2, then, is inside the try block. This code defines three inte- gers: inputValue, which is the number passed from the command line; total, which is the total factorial value; and index1, which is the looping index variable. After doing the string-to-integer conversion using the parseInt static method, the for loop simply loops from one to the command-line supplied value, computing the factorial total as in the COBOL example. Note that the body of the for loop has only one statement in it, so you do not have to code the braces.

The for loop specifies three things, separated with semicolons: the initial value of the index variable, the conditional expression that determines the end of the loop, and the incrementing expression for the loop index. (The ++ operator is used for the latter, as described in the previous chapter.) Compiling and running this code gives the expected results:

>javac TestForLoop.java
>java TestForLoop 3
6
>java TestForLoop 4
24

Let's look closely at the syntax of a for loop in Java. The Java for statement contains all the functionality that COBOL provides. The general format of the Java for statement is as follows:

for (expression)
{
   // statements
}

The statement's expression consists of three main parts, which must be separated by semicolons:

for (initialization; condition; increment)

These pieces are typically used as follows:

  • Initialization is a specification of the initial value of the index variable. (You can also declare that variable here.) Multiple variables can be initialized, separated by commas.

  • Condition is a Boolean expression. The loop continues while the expression is not false, which is evaluated at the top of the loop on each iteration. The loop executes zero times if the expression is false on the initial run. Typically, this is a comparison of the index value to a predetermined maximum value, such as the dimension limit on an array (table).

  • Increment is an assignment or increment statement of your choosing, but typically increments the index value. This part is executed at the end of every iteration. Multiple statements are permitted here, as long as they are comma-separated.

In Java, all management of the loop indexing is your responsibility. The only thing Java gives you in terms of built-in support in the for loop is the promise to keep looping as long as the Boolean expression is not false. Absolutely everything else is up to you. So, if you want to have an index variable and increment it per loop iteration, it is your job to take care of that.

Consider an example of a typical simple for loop in Java that loops through all elements of an array (table) and prints out the contents. Java arrays start at index zero, and contain a built-in public variable called length, which holds the number of items in the array:

char myCharArray[] = new char[20]; // declare array of 20 char
int  idx;
for (idx = 0; idx < myCharArray.length; idx++)
{
    myCharArray[idx] = ' '; // set idx'th character to blank
}

This example starts by declaring an array of 20 characters and declaring an integer index variable named idx. (Arrays are covered in chapter 6.) The for loop, initializes the indexing variable to zero (idx = 0;), loops as long as the idx is less than the array limit (idx < myCharArray.length;), and increments idx by one after each iteration (idx++). The body of the for loop assigns a blank character to each array position.

The following example runs backwards through the array:

char myCharArray[] = new char[20]; // declare array of 20 char
int  idx;
for (idx = myCharArray.length-1; idx >= 0; idx—)
{
    myCharArray[idx] = ' '; // set idx'th character to blank
}

This example initializes the indexing variable to the size of the array minus one (to account for zero-based indexing in Java arrays), and decrements the variable by one until zero is reached. Notice that, in both examples, you have declared the index variable outside of the for loop. You can actually do this as part of the initialization section of the for loop, as follows:

char myCharArray[] = new char[20]; // declare array of 20 char
for (int idx = myCharArray.length-1; idx >= 0; idx—)
{
    myCharArray[idx] = ' '; // set id'th character to blank
}

This code declares and initializes the idx variable in one shot. While this is handy, because of Java's scoping rules, the variable is not accessible outside the for loop. It is accessible in all three parts of the for-loop expression, however, and in the body of the loop itself.

Another point to remember about the for loop is that, like all Java multi-statement structures, the braces are optional if the block or body has only one statement, for example:

for (int idx = 0; idx < salaries.length; idx++)
   System.out.println("Salary is: " + salaries[idx]);

Again, we caution you about not using braces due to the risk of adding additional statements later and forgetting the braces (in which case the first statement remains the only one executed per loop iteration).

As it turns out, all three parts of the for-loop expression are optional. You could legally define a for loop with none of the three parts (although the two delimiting semicolons are still required):

for ( ; ; )
System.out.println("looping...");

What does this mean? Because Java agrees to loop as long as the condition expression is not false, this loop will simply run forever. Of course, we humbly recommend not writing infinite-loop programs! What is very interesting about the for-loop construct, though, is that it is a completely general construct—that is, the initialization part can contain any statement, the conditional part can contain any Boolean expression, and the increment part can contain any statement. It is merely convention—although a convention we recommend you follow—that these are used for index initialization, loop termination checking, and index incrementing or decrementing. The initialization part and the incrementing part actually allow multiple statements to be specified. They must be separated by commas, not the usual semicolons, since these are already used to delimit the three primary parts. This gives tremendous flexibility.

For example, let's go back to the for loop that initialized a character array to all blanks. Because the body of the loop is very small, you actually have the option of performing it in the third part of the for-loop expression, as follows:

for (int idx = 0; idx < myCharArray.length;
      myCharArray[idx]=' ', idx++)
{
}

Notice that the assignment statement is put together with the idx++ statement, separated by commas, in the increment part. This is perfectly legal, and you will see it done often. Because all the necessary work is done in the increment part, there is no need for a body, just an empty block. Alternatively, you could also use an empty statement—a simple semicolon. To be really concise, you could also increment the idx variable right in the assignment statement, as follows:

for (int idx = 0; idx < myCharArray.length;
      myCharArray[idx++]=' ')
   ;

You will often see this kind of compact for-loop statement in Java code. There is a trick to its interpretation. The expression idx++ uses the current value of idx as the index into the array, and then increments it after.

Compact for statements are also commonly used to find the first non-blank character in a character array, as follows:

char myCharArray[] = {' ', ' ', 'a', 'b', 'c'};
int idx;
for (idx = 0; myCharArray[idx] == ' '; idx++)
;
System.out.println("first blank char position = " + idx);

In this example, the index variable idx is declared outside the for loop so it can be accessed later. The for loop then initializes the index to zero, loops as long as the array character indexed by it is blank, and increments the index each time. In this case, the result is 2.

Note that myCharArray is declared as an array of characters and initialized to contain the five characters blank, blank, a, b, and c. This implicitly sets its length to five. The number 2 that you get as a result is zero-based; it is the third position. Of course, if the input is an array of all blank characters, this example runs into trouble. The terminating condition stays true until the index variable is beyond the limit of the array. This results in a runtime exception error (discussed in chapter 10):

java.lang.ArrayIndexOutOfBoundsException: 5

To fix this, you need the condition expression to also check for the end of the array:

for (idx = 0;
   idx < myCharArray.length && myCharArray[idx] == ' ';
   idx++)
      ;

Now you have a robust and compact example to check for the first non-blank character, with each of the three parts of the for-loop expression on its own line. Because Java is a free-format language, this is perfectly legal and is occasionally done for readability. Your subsequent code should check if idx is greater than the array's length, which will determine if the array is all blanks or not:

if (idx >= myCharArray.length)
   System.out.println("All blank array!");
else
   System.out.println("first blank char position = " + idx);

How would you find the last non-blank character, which is another common programming requirement? The procedure follows:

for (idx = myCharArrar.length-1;
     idx >= 0 && myCharArray[idx] == ' ';
     indx—-)
  ;

In Java, you can have multiple-dimension arrays, as discussed in chapter 6. Imagine, then, that you have a two-dimensional array called raster, and that you want to initialize it to 1 for each position. The for loop can handle this easily. It is an array's best friend! Two-dimensional arrays are declared using two sets of empty brackets and the new opera- tor, which specifies the length for each dimension. Note that arrayVariable.length gives the length of the first dimension, and arrayVariable[x].length gives the length of the xth row. Listing 4.3 provides a fully compilable example.

Listing 4.3: A Java example of a two-dimensional array.
Start example
public class TestFor
{
   public static void main(String args[])
   {
      int raster[][] = new int[3][2]; // matrix 3 by 2
      for (int x=0; x < raster.length; x++)
      {
         for (int y=0; y < raster[x].length; y++)
         {
            raster[x][y] = 1;
            System.out.println("raster " + x + "," + y + " = " +
                        raster[x][y]);
         }
      }
   } // end main method
} // end TestFor class
End example

This results in the following output:

raster 0,0 = 1
raster 0,1 = 1
raster 1,0 = 1
raster 1,1 = 1
raster 2,0 = 1
raster 2,1 = 1

This example illustrates the ability to nest for loops. That is, the body of one for loop can be yet another for loop. The inner loop is executed for its duration multiple times, once per iteration of the outer loop. The example in Listing 4.3 is a little bit longer than necessary. Here is the smallest possible version of this nested for loop for initializing a two-dimensional array (without the println statement):

for (int x=0; x < raster.length; x++)
   for (int y=0; y < raster[x].length; raster[x][y++] = 1)
      ;

Looping for a While

Now that you have mastered the for loop (right?), you will find the while loop to be a breeze. It is a subset of the for loop's functionality and brings nothing new to the table except ease of use. The while statement is a lot simpler than the for statement because the while loop only takes an expression for evaluation and does not have as many parameters as the for loop allows. It is used when the termination of the loop is not predetermined, and you simply want to loop while a given condition is true. You decide in the body of the code when to set that condition to false. (If this procedure is not performed correctly, of course, you will have an infinite loop.) This is called an indeterminant loop because you can't predict when it will end. It is especially useful when reading or asking for input, because the end will be determined by the end of the file or by a user-initiated action.

COBOL uses the PERFORM UNTIL statement to loop while the conditional expression is true. The test of the conditional expression is performed at the beginning of each iteration of the loop, and as a result it is possible the loop will not perform at all if the expression is false on the way in.

For this example, imagine your son or daughter asks you for a loan, and you want to give them a printout of the payment schedule. Because you are a softee for your kids, you not only allow them to pay the load back in easy $100 monthly increments, you don't charge them any interest, either. (We know, we know—you have no "interest" in such a loan! However, it is the "principal" that is important here.)

Listing 4.4 shows a little COBOL program involving the PERFORM UNTIL statement. Given a total amount as a parameter, this program will print out, for each month, the total paid, and the amount remaining after the payment.

Listing 4.4: An example of COBOL's PERFORM UNTIL statement.
Start example
IDENTIFICATION DIVISION.
PROGRAM-ID.     TSTUNTIL.
AUTHOR.         Phil Coulthard.
******************************************************
* Test the PERFORM...UNTIL statement                *
******************************************************
DATA DIVISION.
WORKING-STORAGE SECTION.
   01 TOTAL     PIC 999 VALUE ZEROS.
   01 PAYMENT   PIC 999 VALUE 100.
   01 REMAINING PIC 999.

LINKAGE SECTION.
   01 INPUT-VALUE PIC 99.

PROCEDURE DIVISION USING INPUT-VALUE.

000-MAIN.
   COMPUTE REMAINING = INPUT-VALUE - TOTAL.
   PERFORM UNTIL REMAINING <= 0
      IF ((TOTAL + PAYMENT) > INPUT-VALUE) THEN
         COMPUTE PAYMENT = INPUT-VALUE - TOTAL
      END-IF
      COMPUTE TOTAL = TOTAL + PAYMENT
      COMPUTE REMAINING = INPUT-VALUE - TOTAL
      DISPLAY TOTAL ", " REMAINING
   END-PERFORM
   DISPLAY "DONE".
   STOP RUN.
END PROGRAM TSTUNTIL.
End example

This program loops while there is a remainder left to pay. For each iteration of the PERFORM UNTIL loop, it first checks if the new payment will reduce the debt to zero or below. If so, it changes that month's payment from the default $100 to whatever is left. Then the logic, which is the same for all months, computes the total paid so far by adding this month's payment, and the amount remaining after this payment is made. Finally, the total paid and the total remaining are printed. If you want, you can code, compile, and test this by running it with numbers like 435 and 400.

The while loop for both Java and COBOL executes zero or more times. This point becomes important in contrast to the next section, where the do…while loop is described, which executes one or more times.

The equivalent in Java to COBOL's PERFORM UNTIL statement is the while statement, which has the following syntax:

while (expression)
{
   // statement(s)
}

The expression is any Boolean expression evaluating to true or false. The loop iterates as long as the expression is true. For example, Listing 4.5 is the Java equivalent of the previous COBOL example.

Listing 4.5: The Java equivalent of the COBOL code in Listing 4.4.
Start example
public class TestWhileLoop
{
   public static void main(String args[])
   {
      if (args.length == 0)
         System.out.println("You must pass a parameter");
      else
         {
            try
            {
               int inputValue = 0;
               int total      = 0;
               int payment    = 100;
               int remaining;

               inputValue = Integer.parseInt(args[0]);
               remaining = inputValue - total;
               while (remaining > 0)
               {
                  if ((total + payment) > inputValue)
                  payment = inputValue - total;
                  total = total + payment;
                  remaining = inputValue - total;
                  System.out.println(total + ", " + remaining);
               }
               System.out.println("Done");
            }
            catch (NumberFormatException exc)
            {
               System.out.println("Sorry, not a valid number");
            }
         }
      } // end main
} // end of class TestWhileLoop
End example

Compiling and running this listing produces the following:

>javac TestWhileLoop.java
>java TestWhileLoop 435
100, 335
200, 235
300, 135
400, 35
435, 0
Done
>java TestWhileLoop 400
100, 300
200, 200
300, 100
400, 0
Done

In Java, the expression is very often a variable of type Boolean, which is a variable type that can only hold true or false. This makes it a perfect match for the condition expression. The variable is usually declared and initialized to true first, then set to false inside the loop when some ending condition is met, such as reading the end of file:

boolean notDone = true;
while (notDone)
{
   // read input
   if (input.eof()) //end of file for input?
   notDone = false;
}

You will usually see the Boolean variable initialized to false and its negation checked in the condition expression, which accomplishes the same thing:

boolean done = false;
while (!done)
{
   // read input
   if (input.eof()) //end of file for input?
   done = true;
}

The while loop is actually equivalent to the for loop, in that one can be completely mapped to the other. All you do is move the initialization part of the for loop outside, preserve the condition expression, and move the incrementing part to the bottom of the while loop's body. For example, here is a typical for loop:

for (int x = 0; x < myArray.length; x++)
{
   myArray[x] = 1;
}

Here is the exact equivalent, but as a while loop:

int x = 0;
while (x < myArray.length)
{
   myArray[x] = 1;
   x++;
}

So, when do you use the for loop, and when do you use the while loop? It is your decision, really, because they are functionally equivalent. In general, though, the for loop is used when you can predict the number of iterations of the loop, and while is used when you cannot.

Looping Until Done

The last loop structure to cover here is the do…until loop. This is the PERFORM WITH TEST AFTER…UNTIL statement in COBOL, and the do…while statement in Java. The difference from the previous loop discussions is that the conditional expression is evaluated at the end of each loop iteration.

Listing 4.6 shows a revised version of the previous example, using WITH TEST AFTER for the loop (only the main paragraph is shown). The only major change here, after adding the phrase WITH TEST AFTER, is a move of the initial computation for REMAINING from outside the loop to inside the loop. Because the expression is evaluated at the end, there is no need to "prime" the variable before entering the loop. A check for the remainder going to or below zero is also added, since that can now happen inside the loop, and the now-redundant REMAINING computation is deleted at the bottom of the loop.

Listing 4.6: A COBOL example of a do…until loop structure.
Start example
000-MAIN.
   PERFORM WITH TEST AFTER UNTIL REMAINING <= 0
      COMPUTE REMAINING = INPUT-VALUE – TOTAL
         IF (REMAINING > 0) THEN
            IF ((TOTAL + PAYMENT) > INPUT-VALUE) THEN
               COMPUTE PAYMENT = INPUT-VALUE - TOTAL
            END-IF
            COMPUTE TOTAL = TOTAL + PAYMENT
*           COMPUTE REMAINING = INPUT-VALUE – TOTAL
            DISPLAY TOTAL ", " REMAINING
         END-IF
         END-PERFORM
         DISPLAY "DONE".
         STOP RUN.
End example

If the two versions of PERFORM UNTIL are so similar, why have both of them? The answer is simply one of flexibility. Really, you can get by with either one, but having both allows you to choose whichever one is most convenient for the job at hand.

The Java equivalent is do while. Here is the syntax for it (note the ending semicolon):

do
{
   // statement(s)
} while (expression);

Listing 4.7 shows the previous Java example revised to use do…while (just the loop is shown; the rest is unchanged):

Listing 4.7: A Java example of a do…while structure.
Start example
inputValue = Integer.parseInt(args[0]);
do
{
   remaining = inputValue - total;
   if (remaining > 0)
      {
      if ((total + payment) > inputValue)
         payment = inputValue - total;
      total = total + payment;
      //remaining = inputValue - total;
      System.out.println(total + ", " + remaining);
   }
} while (remaining > 0);
End example

The only major change is to move the initial computation for remaining into the loop, since the expression evaluation is at the bottom of each loop iteration now. Also, the redundant statement at the bottom of the loop is deleted, and a check is added to ensure the logic is not executed if the remaining value is zero or less.

As with COBOL, Java's do while statement is functionally identical to the while statement. The difference that the condition expression is not evaluated until the end of the loop, which guarantees at least one iteration of the loop. Our experience is that programmers tend to use one or the other of these exclusively. That underlines their equivalent functionality.

Consider the job of reading input from a file. (You have done that, right?) You have to read until the end of the file. Which loop structure is best? That's up to you. Both have pros and cons. If you use while, you must do the initial read outside the loop to initialize the end-of-file flag. Then, you must do the subsequent reads inside the loop, as follows:

boolean eof; // a variable that will be true or false
eof = myFile.getRecord(); // get first record from file
while (!eof)
{
   // ... process record
   eof = myFile.getRecord();
}

The disadvantage here is clear: you have to duplicate the record read statement. That can create bugs later, if that statement is changed in one place but not the other. Alternatively, you could code this using do while, and avoid the redundancy, as follows:

boolean eof; // a variable that will be true or false
do
{
   eof = myFile.getRecord();
   if (!eof)
   {
      //... process record
   }
} while (!eof);

The disadvantage of this approach is that the if statement adds complexity to ensure that the last read does not result in end-of-file. Again, the choice is a personal decision. (Our preference is while versus do while, but what do we know!)


Transfer Structures

Let's now turn to statements that directly alter the flow of control, by explicitly transferring control to another statement.

If You Continue, We Will Need a Break

You have seen the looping constructs in COBOL and Java, and how their termination is determined by a condition expression at the top or bottom of the loop. The language runtime evaluates the expression at each iteration of the loop, and continues iterating as long as the expression evaluates to true. Controlling when the loop ends is usually a simple matter of setting the appropriate variables to force the expression evaluation to false. This is the preferred and structured approach.

For reasons of completeness, the Java language offers an alternative shortcut to this, in the form of the break statement. When used in the body of a switch, while, do, or for structure, it forces the immediate end of the loop. Control is then passed by default to the statement after the loop structure. The Java break statement also allows an optional tag parameter to force the exit of a loop labeled with that tag. In addition, Java offers a shortcut to force iteration of the loop from within the body, thus skipping all subsequent codes in the body. This is done by using the continue statement in Java, which, like the break statement, allows an optional tag value to explicitly identify the loop to be iterated. The continue statement is valid inside while, do, and for structures in Java.

Both break and continue apply to the innermost loop structure. That is, they exit or iterate the innermost loop. In a nested loop, to exit or iterate a more outer loop, you use the optional tag value to skip to a labeled outer loop.

Note

Both of these constructs for forcing termination and iteration offer nothing new in function over a little bit of proper if-else coding, and can be avoided.

They make for some nasty maintenance problems, and are really nothing but special case go-to statements. Some people avoid them altogether. Other people insist that they can offer a more readable and elegant-looking solution than nasty use of conditioning. Our preference is to not use them.

Let's start with simple examples that do not use labeled loops and target labels on the break and continue statements. Specifically, let's start with the break statement.

Do you recall the factorial example from earlier in this chapter? Imagine that you want to enhance it so that instead of acting on the single number passed via the command line, it continually prompts the user for the next number to factorialize. The code keeps computing and displaying the factorial of the input number until the user types in the letter Q, say.

In this case, you might put the factorial algorithm in a separate static method that takes the input number as a parameter, and then the main method will focus on asking for the next number, calling the factorial method on it. It ends when the user enters a Q. The main method will have a never-ending loop, which can simply be coded as while (true). It will, however, need a way to exit that loop when it encounters a Q as input. That is where you use the break statement, which breaks out of the loop. Listing 4.8 shows the full example.

Listing 4.8: Using Java's break statement.
Start example
import java.io.*; // for BufferedReader class in java.io package
public class Factorial
{
   public static void main(String args[])
   {
      System.out.println("Welcome to class Factorial");
      int inputValue  = 0;
      int outputValue = 0;
      while (true)
      {
         // prompt for, and read, user input from console...
         System.out.println("Enter number or Q to quit: ");
         String inputLine = "read failed";
         BufferedReader d = new BufferedReader(
                     new InputStreamReader(System.in));
         try {
            inputLine = d.readLine();
            if (!inputLine.equalsIgnoreCase("Q"))
            {
               // compute and display factorial
               inputValue = Integer.parseInt(inputLine);
               outputValue = factorial(inputValue);
               System.out.println("Factorial = " + outputValue);
            }
         else
            break;
         } catch (Exception exc) {}
      }
   } // end main
   /** Compute and return factorial of given number */
   public static int factorial(int inputValue)
   {
      int total = 1;
      for (int index1=1; index1 <= inputValue; index1++)
      total = index1 * total;
      return total;
   } // end of method factorial
} // end of class Factorial
End example

There is some code in Listing 4.8 that you are not expected to be able to decipher yet, such as the processing necessary to accept input from the user via the console, but the key code is inside the try block in the middle of the main method, which checks if the user-supplied input is the letter Q by using the Java-supplied equalsIgnoreCase method of the String class. If it's not Q, the factorial method is called to print out the result. If it is Q, the break statement exits the loop, which in this case is the while(true) loop. This subsequently leads to the end of the main method, ending the program. Here is the output:

>java Factorial
Welcome to class Factorial
Enter number or Q to quit:
3
Factorial = 6
Enter number or Q to quit:
4
Factorial = 24
Enter number or Q to quit:
10
Factorial = 3628800
Enter number or Q to quit:
q

The break statement is typically used to force your way out of deeply nested loops, and is in contrast to the procedure of setting a variable and checking that variable in the remain- der of the code in all structures. It is not unlike using a combination of GO TO and EXIT in a COBOL program to force the premature exit of a paragraph.

The continue statement does not exit the current loop. Rather, it forces another iteration of the loop, causing the conditional expression to be evaluated as though the end of the loop body had been reached. For example, imagine you are processing an array of numbers, each of which you want to perform the factorial method on, as before. You will change the example to handle this, and for simplicity just hard-code the array. The trick, though, is that you want to avoid or "skip over" array entries that contain a negative number or zero. For these, you can just use continue instead, and force another iteration of the loop. Listing 4.9 provides the code, with just the main method shown.

Listing 4.9: Adding the continue statement to the break structure.
Start example
public static void main(String args[])
{
   System.out.println("Welcome to class Factorial2");
   int arrayOfNumbers[] = {3, -3, 4, -4, 25, -25, 0};
   // hard-coded array
   int inputValue  = 0;
   int outputValue = 0;
   for (int idx = 0; idx < arrayOfNumbers.length; idx++)
   {
      if (arrayOfNumbers[idx] <= 0)
         continue;
      else
         {
            // compute and display factorial
            inputValue = arrayOfNumbers[idx];
            System.out.println("Input = " + inputValue);
            outputValue = factorial(inputValue);
            System.out.println("Factorial = " + outputValue);
         }
   } // end for loop
} // end main
End example

The important code here is to check if the array value is less than or equal to zero, and if so the use of continue to force iteration of the loop. This example might lead you to believe that Java's continue is the same as COBOL's CONTINUE, since both would work in this example. However, this is only true because no other executable code exists between the end of the else body and the end of the loop body. If there were, Java would not execute it, while COBOL would. You see, COBOL's CONTINUE is really just a statement that means do nothing, versus iterate the loop.

The break statement exits whatever loop you are currently in, and the continue statement forces an iteration of whatever loop you are currently in. Sometimes you are inside more than one loop, if the loop is nested. For example, nested for loops are often used to process the entries in a two-dimensional array. The outer loop walks the rows of the array, and for each row, the inner loop walks the columns of that row. In these cases, a break statement can be forced to exit the outer loop versus the default, which is the inner loop. To do this, you must label the outer loop using the label: syntax, and then explicitly name that label on the break statement, as in break label;.

For example, suppose you are running a school in which all the final marks for each student are recorded in an array indexed by student number, and these arrays are further nested inside an array of all classes. At the end of each semester, you want to determine the average mark per class and the overall school average. To do this, you walk through each class in the outer dimension of the array, and for each entry, you walk through each student in the inner dimension of the array, to accumulate the totals and compute and display the averages.

The trick, though, is that while every class can have a maximum of 10 students, some have less. So, you make the innermost dimension 10, but identify the end of the array with a special value of –1. That way, you know to end processing when you first see a mark of –1. Also, sometimes a student quits a class part of the way through the semester. In that case, his or her entry in the array is flagged with a special value of –2. When this value is found, the entry should be skipped.

Listing 4.10 shows the code for this example. (Some sample class marks were hard-coded for example purposes here.) Don't worry about the syntax of the arrays for now; they are covered in chapter 6. What is important to note here is simply the way the value of –1 is handled in the innermost loop, with a jump right out of the innermost loop and a forced iteration of the outermost loop. This is done because, by our own definition, there are no more marks for this class to process. To accomplish this jump, continue outer is used, where outer is the label placed in front of the outermost for loop. Notice also that, for the value of –2, continue is used with no label specified to force iteration of the inner loop. The default is to continue iterating the current innermost loop.

Listing 4.10: Using a break statement with the outer and inner dimensions of an array.
Start example
public static void main(String args[])
{
   System.out.println("Welcome to class AverageMarks");
   int marksByClass[][] = { {70, 80, 55, 26, 30, 50, 95, 78, 87, 65},
                           {67, 76, 73, 68, -2, 78, 81, 65, -1, -1},
                           {87, 78, -2, 92, 71, 88, 98, 91, 67, -1},
                           {56, 65, 68, 72, 65, 76, 70, -1, -1, -1},
                           {67, 87, 93, 81, 78, 90, -1, -1, -1, -1} };
   int totalPerClass[] = {0,0,0,0,0};
   int countPerClass[] = {0,0,0,0,0};

   outer: for (int classes = 0; classes < marksByClass.length; classes++)
   {
      for (int marks = 0; marks < marksByClass[0].length; marks++)
      {
         if (marksByClass[classes][marks] == -1)
            continue outer;
         else if (marksByClass[classes][marks] == -2)
         continue;
         totalPerClass[classes] += marksByClass[classes][marks];
         countPerClass[classes] += 1;
      } // end inner for loop
   } // end outer for loop

   // print averages:
   int total = 0;
   int count = 0;
   for (int classes=0; classes<totalPerClass.length; classes++)
   {
      int average = totalPerClass[classes] / countPerClass[classes];
      System.out.println("Average for class = " + average);
      total += totalPerClass[classes];
      count += countPerClass[classes];
   }
   System.out.println("Average for all classes = " + (total / count));
   System.out.println("Total number of marks = " + count);
} // end main
End example

By the way, if you are curious, the results of running this example are as follows:

>java AverageMarks
Welcome to class AverageMarks
Average for class = 63
Average for class = 72
Average for class = 84
Average for class = 67
Average for class = 82
Average for all classes = 73
Total number of marks = 38

One thing you should notice about the continue and break examples is that you could easily do without continue and break!

Go to Where?

The purpose of control structures is to tightly control the flow of logic so that complex problems become simple to follow. With transfer structures, you can pass the flow to another part of a program. In structured programming, this transfer is usually at the exit point of a control structure or to a well-defined point in a program. The transfer logic is limited, and easy to follow. The go-to statement is an example of a transfer structure that does not follow structured programming design. It is the root of what is termed "spaghetti code," which refers to code that flows all over the place and does not seem to have any predictable path. With the advent of structured programming practices and language support, the use of go-to has fallen out of favor. Java designers decided to not even include it in the language, although the word is reserved. It is interesting that they took such a strong stand on go-to while still advocating the use of break and continue, which are quite similar.

COBOL still has a GO TO statement for historical reasons. You can use it to jump directly and permanently to a paragraph. The COBOL GO TO statement is quite robust, with three permutations, a legacy of days gone by (hopefully). The version that scares us the most is the altered GO TO—not only does it arbitrarily transfer control, but you cannot even tell where it transfers control to until runtime. What a completely unstructured and clearly evil-to-the-bone statement that is! Other than that, we have no opinion.

Return to Caller

In COBOL, you exit from a program and return to the caller by coding GOBACK, EXIT PROGRAM, or STOP RUN. Typically, EXIT PROGRAM is for exiting a subprogram (a called program), while STOP RUN is for exiting the main program as it completely ends the run unit. The GOBACK statement is the most versatile, equivalent to EXIT PROGRAM in a subprogram, and to a STOP RUN in a main program.

All these statements in COBOL are simple and take no parameters or values. So how is information returned to the caller? Well, you can either update shared data items (EXTERNAL for peer programs or GLOBAL for nested programs), or update one of the passed parameters identified in the USING phrase of the PROCEDURE DIVISION header statement.

Another option is to return a value to the caller using the RETURNING phrase of the PROCEDURE DIVISION. (For OS/400 programmers, this is only available in ILE COBOL when calling ILE procedures, not program objects.) On this, you identify a LINKAGE SECTION data item, and at exit that data item's value is returned to the caller. That caller must have specified a RETURNING phrase on its CALL statement, identifying the data item in its program that will contain the returned value, after the call. As an example of this, Listing 4.11 shows the factorial program from earlier in this chapter, but designed to return the factorial result to the caller.

Listing 4.11: A COBOL example to return a result back to the caller.
Start example
IDENTIFICATION DIVISION.
PROGRAM-ID.     FACTORL.
AUTHOR.         Phil Coulthard.
******************************************************
* Returns factorial of given integer                 *
******************************************************
DATA DIVISION.
WORKING-STORAGE SECTION.
   01 INDEX1  PIC 999 VALUE ZEROS.
   01 TOTAL   PIC 99999 VALUE 1.
LINKAGE SECTION.
   01 INPUT-VALUE PIC 999.
   01 OUTPUT-VALUE PIC 999.

PROCEDURE DIVISION USING INPUT-VALUE RETURNING OUTPUT-VALUE.
000-MAIN.
   PERFORM VARYING INDEX1 FROM 1 BY 1
         UNTIL INDEX1 > INPUT-VALUE
      COMPUTE TOTAL = INDEX1 * TOTAL
   END-PERFORM
   MOVE TOTAL TO OUTPUT-VALUE.
   GOBACK.
END PROGRAM FACTORL.
End example

You have seen this example written as a method in Java before, in Listing 4.8, but it is shown again in Listing 4.12, for completeness. Java, as you can see, uses a return statement to end a method. It simply returns control to the point where the method was called, whether that is in the same class or in another class.

If the method is defined as returning a value, such as int in this case, then the return statement must specify a value after it, which will be returned to the caller. That value can be a literal, a variable, or an expression. The only rule is that the type of the value must match the type defined as the return type for this method.

Listing 4.12: Using a return statement to end a method in Java.
Start example
/** Compute and return factorial of given number */
public static int factorial(int inputValue)
{
   int total = 1;
   for (int index1=1; index1 <= inputValue; index1++)
      total = index1 * total;
   return total;
} // end of method factorial
End example

Java also allows return with no value at all. The empty return statement must be in a method declared with the void return type, or in a constructor. In Java, a method that returns nothing does not have to explicitly code a return statement. It is implicit when the end of the method is reached, just as GOBACK is implicit at the end of a COBOL program.

You can specify more than one return statement in a method, perhaps for different conditions. For example, a method that returns the maximum of two given numbers might look like this:

int max(int value1, int value2)
{
   if (value1 > value2)
      return value1;
   else
      return value2;
}

This is a common practice and, in this case, is perfectly acceptable. Be careful, though, in cases where you have common code to be executed before returning. You must duplicate that code for every embedded return condition, an error-prone practice. So, as with the use of break and continue, using multiple return statements in a single method is some- thing to be done sparingly.

An alternative implementation to the max method with a single return statement would be the following:

int max(int value1, int value2)
{
   int returnValue;
   if (value1 > value2)
      returnValue = value1;
   else
      returnValue = value2;
   return returnValue;
}

This is more code, but, arguably, it is safer in the long run.

In Java, you can specify a return statement in your main method as well, which signifies the end of the application. Again, if no return statement is specified, it is implicit at the end of the main method.

Sometimes, you want to not only return from a method, but also end the whole thing, just like STOP RUN does in COBOL. For example, Java code might be processing the Exit menu item in a GUI application. A simple return won't do much here. Instead, you must use a static method supplied in the System class to force the immediate exit of the run unit or Java Virtual Machine for this application. That method is exit, and it takes an integer value that other programs can access if they wish. The convention is to pass zero for valid exit and one for premature exit. So, for example, to process an exit request from the user, you would use the following:

System.exit(0);

And with that, the ultimate exit, we exit this chapter! Take a break, then GOBACK to reading!


Summary

This chapter introduces you to control flow statements in Java and compares them, one at a time, with COBOL's, to illustrate the following:

  • COBOL and Java have the common structured statements available in most modern programming languages, including the following in Java:

    • The if and else structures for binary conditionals.

    • The switch statement for multiple conditionals.

    • The looping statements for, while, and do-while.

    • The break and continue statements for premature loop exit and iteration.

  • Use the return statement for returning from Java methods.

  • Use System.exit(0) to exit completely from the JVM.

  • Use the if statement if the number of test are few, or the ?: statement for two tests.

  • Use the switch statement in Java if the number of tests are many.

  • COBOL's EVALUATE statement is more powerful than Java's switch statement, since it allows you to specify any kind of expression with any category. In Java's switch statement, the expression can only be a byte, char, short, or int variable.

  • Use the for loop when you have a predicable number of iterations you want the loop to go through.

  • Use the while loop when you want to iterate zero or more times through the loop.

  • Use the do-while loop when you want the body of the loop to execute at least once.

  • Use break and continue sparingly in Java, for forcing exit or iteration of a loop.

  • The GO TO statement is available in COBOL, but not in Java.

  • Java supports other transfer statements, including try/catch and throw, for dealing with exceptions. These are discussed in chapter 10.


Chapter 5: Data Types And Variables

Overview

Abasic part of most languages today is the offering of a variety of standard data cat-egories or data types, such as binary, integer, float, character, and so on. As with COBOL data items, in Java, the type of every variable used must be declared at compile time. COBOL uses the terms data category and class for this, while Java uses the term data type. Similarly, COBOL uses the term data item, where Java uses the term variable. When speaking generically, we use data type and variable.

A data type defines the set of values that an expression can produce, or that a variable can store or contain. The data type of a variable establishes the operations that may occur on the variable. For example, if you declare a variable as a character, you cannot perform arithmetic operations on it. If you declare a variable as numeric (no matter what type of numeric it is), you cannot do string manipulations on it, such as concatenate or scan. If you do attempt to manipulate a data item in a way that is inconsistent with its type, the compiler will inform you of the problem by issuing an error message at compile time. COBOL and Java have different forms of data declarations, discussed later in this chapter.

Both COBOL and Java are strongly typed languages. This means every variable and, implicitly, every expression, has a type that must be known at compile time. This allows the compiler to determine what operations can be used with certain variables, and what values can be stored in them.


Review Of COBOL Data Categories

In COBOL, the PICTURE and/or USAGE clauses specify the data category of an elementary data item. We use the term category to refer to data items of a particular type, for which only specific values and specific operations are permitted. In COBOL, the category of data determines the permitted values and operations. The class of data is a grouping of categories that determines the permitted values and operations for that grouping of categories. So, you can think of category as a data type and a class as a more inclusive data type. Table 5.1 depicts the categories available in COBOL.

Table 5.1: COBOL Categories for Data Items.
Data Category | OS/390 | OS/400 | Description
numeric | yes | yes | Holds numeric data only, with or without an assumed decimal point. If there is an assumed decimal point, its position is fixed. Internally, can be stored as binary (two's compliment) or packed decimal (one digit every four bits).
numeric-edited | yes | yes | Holds numeric data with additional explicit character data, such as a decimal point, commas, currency symbol, or sign. Internally stored one byte per digit.
alphabetic | yes | yes | Holds alphabetic characters only.
alphanumeric | yes | yes | Hold characters or digits 0 to 9.
alphanumeric-edited | yes | yes | Holds alphanumeric data with additional explicit character data.
DBCS | yes | yes | Holds double-byte character set for Japanese, Chinese, Simplified Chi-nese, and Korean characters.
external floating-point | yes | yes | Holds floating-point values that contain a mantissa and exponent, intended for external display. USAGE DISPLAY.
pointer | yes | yes | Holds a pointer to allocated memory.
procedure pointer | yes | yes | Holds a pointer to a procedure (program) that can be the target of a CALL.
object reference | yes | no | Holds a reference (pointer) to an object in OO COBOL. (See Appendix C for more information.)
index name | yes | yes | Holds an index offset value that is used only for accessing elements of a table. Defined with INDEXED BY.
index | yes | yes | Holds an index offset value that is used only for accessing elements of a table. Defined with USAGE IS INDEX
boolean | no | yes | Holds a bit value of B"1" or B"0".
date | yes | yes | Holds a date. (See chapter 8 for more information.)
time | no | yes | Holds a time. (See chapter 8 for more information.)
timestamp | no | yes | Holds a date and a time. (See chapter 8 for more information.)
typedef | no | yes | Defines this group item as a type that can subsequently be specified as the type of another data item. OS/400 only.
internal floating-point | yes | yes | Holds floating-point values that contain a mantissa and exponent, intended for internal computations. Can be single or double precision. USAGE COMP-1 or -2.


Introduction To Java Types

Variables in Java, like data items in COBOL, must be defined with a type. Java has two distinct categories of types:

  • Primitive types are similar to COBOL's categories in that they are predefined in the language and have built-in language support in the form of operators and conversion rules.

  • Reference types are variables whose type is defined to be that of a class in Java, either Java-supplied or user-defined. Reference variables, as shown in chapter 2, are equated to an instance of the class type by which they are defined. Because they contain the memory address of a class instance or object, they are said to refer to an object; hence, the term reference variable. Reference variables distinguish Java as an object-oriented language, compared to the primitive-only data types in standard COBOL (versus OO COBOL).

In this chapter, the discussion is limited to primitive types in Java, which compare to CO-BOL's categories. Reference types, introduced in chapter 2 are discussed in more detail in chapter 9.

All of Java's primitive types are also available as classes in the java.lang package. These classes are wrappers of the primitive types, with methods for converting to and from them. They are of value in those cases where the code expects only objects. Also of interest is that arrays (tables) and strings are both implemented as objects in Java, so variables of these are actually reference variables. Arrays and strings are discussed in the next two chapters. Table 5.2 displays the eight primitive data types in Java.

Table 5.2: Java Data Types.
Data Type | Description
byte | Numeric signed, one byte of storage. Holds integers from –128 to +127.
short | Numeric signed, two bytes of storage. Holds integers from –32768 to +32767.
int | Numeric signed, four bytes of storage. Holds integers from –2,147,483,648 to +2,147,483,647 (about two billion).
long | Numeric signed, eight bytes of storage. Holds integers from –2**63 to +(2**63)-1.
char | Numeric, officially unsigned, but typically holds a single alphanumeric character. Note characters in Java are Unicode based, hence take two bytes of storage.
float | Single-precision floating point, four bytes of storage.
double | Double-precision floating point, eight bytes of storage.
boolean | Holds either true or false (language keywords). One byte of storage.

Primitive data types in Java are subdivided into the following categories:

  • Numeric types consist of those that do not have decimals and those that do. The former are in the integer subcategory, and include one-byte (byte), two-byte (short), four-byte (int), and eight-byte (long) types. The latter are in the float subcategory, and include four-byte (float) and eight-byte (double) types. Java's numeric data types are signed, allowing both positive and negative numbers.

  • The character data type, unlike COBOL, is always one character long. Chapter 7 describes the way that the String class is used to hold multicharacter strings. Also unlike COBOL, a character in Java is actually two bytes long because it is based on the 16-bit Unicode encoding standard which encompasses all international characters. In contrast, a character in COBOL is one byte long and is based on the EBCDIC encoding standard. In COBOL, then, the use of codepages is required to support international characters properly, but not in Java.

  • The Boolean data type supports two values: true and false. These are reserved words in Java and are only assignable to Boolean variables. Boolean is analogous to OS/400 COBOL boolean data items or DDS indicators, where 1 is true and 0 is false.

Java does not have the notion of edited data items, and so does not distinguish between internal and external representation. Its numeric types (byte, char, short, int, long, float, and double) can only contain numeric values, without editing symbols. If you want to add character embellishments for the purpose of printing or displaying, you do this on your own, using methods such as System.out.println, or the methods in the String or StringBuffer classes described in chapter 7. For example, passing a floating-point variable to System.out.println will result in the appropriate sign for the mantissa and exponent, and the letter E, being added to the output value.

You could also use the toString method in the wrapper classes (such as Float) to convert the numeric variable to a String object, and then use the character-manipulation methods in the String or StringBuffer classes to write your own method to format variables for output. Better yet, there is a class called DecimalFormat in the java.text package, that is explicitly for the purpose of formatting numeric data for display or print purposes. With it, you define a pattern using special symbols, much as you do with the PICTURE clause for numeric-edited data in COBOL. Once you have a DecimalFormat object with an associated pattern, you can apply it to any double or long value and get back the formatted result. A brief comparison of the data types in the two languages is shown in Table 5.3.

Table 5.3: COBOL Versus Java Data Types.
COBOL USAGE Clause | COBOL Digits in PIC clause | Java Data Type | Comments
BINARY or COMP | S9(1) through S9(4) | short | Signed two byte. No implied decimals.
BINARY or COMP | S9(5) through S9(9) | int | Signed four byte. No implied decimals.
BINARY or COMP | S(10) through S(18) | long | Signed eight byte. No implied decimals.
BINARY or COMP | S9(1)V through S9(18)V or S9(31)V on OS/400 | BigDecimal | A Java-supplied class in the java.math package. For numeric values with implied decimals.
COMP-1 |  | float | Single-precision internal floating-point.
COMP-2 |  | double | Double-precision internal floating-point.
DISPLAY or edited | 1 | char | Single character only.
DISPLAY or edited | >= 2 | String | A Java-supplied class in the java.lang package, used for multi-character text data. See chapter 7.
DISPLAY-1 | 1 | char | Single DBCS character only.
DISPLAY-1 | >= 2 | String | Strings and chars can hold SBCS or DBCS data because they are UNICODE-based.
POINTER |  | object reference | Memory addresses. Note that Java does not permit address math.
PROCEDURE-POINTER |  | not available | No way in Java to get a method address or pointer.
OBJECT-REFERENCE |  | object reference | Same in all OO languages.
INDEX |  | not available | No explicit index type.
BOOLEAN[2] |  | boolean | 1 = true, 0 = false.
DATE FORMAT[1] |  | Date class | A class in the java.util package. See chapter 8.
FORMAT DATE[2] |  | Date class | A class in the java.util package. See chapter 8.
FORMAT TIME[2] |  | Time class | A class in the java.util package. See chapter 8.
FORMAT TIMESTAMP[2] |  | Date class | A class in the java.util package. See chapter 8.
TYPEDEF[2] |  | any Java class | A class is a typedef, but with the addition of methods.
[1]OS/390 only. [2]OS/400 only.

As you can see, there are some COBOL categories that the Java designers chose to implement as classes versus primitive data types. These are discussed in subsequent chapters. Also note that some data-type comparisons between the languages depend on the defined length of the particular data item in COBOL. We will go into more detail on each of the primitive data types after covering some syntactical details.


Data Declaration

Categories or data types are put to use in two places in a language: in explicit variables that you define; and in implicit or temporary variables the compiler generates to process expressions. Now that you have had a glimpse of what the types are in Java, it is important to know how to define variables of this type. The syntax is completely different from COBOL's, but arguably easier. Let's review both.

Defining Data Items in COBOL

Where and how do you declare a data item in COBOL? As you know:

  • In the FILE SECTION.

  • In the WORKING-STORAGE, LOCAL-STORAGE, or LINKAGE SECTION.

  • By identifying the level number and the name.

  • Using the PICTURE clause and/or the USAGE clause to identify the class, category, and format of the data.

  • Using the DATE FORMAT (OS/390) or FORMAT (OS/400) clause for date data items.

COBOL's level numbers are interesting because Java has nothing similar. In COBOL, as you know, you can define a group data item that contains other group items and elementary items. You can also use level-66 items to rename previously defined items, or level-88 items to create condition names. You are "level" headed!

Defining Variables in Java

In Java, variables can be defined in two places: at the class level so that they are available to all methods (similar to COBOL's data items), or inside a method so that they are available only to the code inside that method (similar to COBOL's nested program data items). This is an important distinction to keep in mind.

Unlike COBOL, Java has only a single syntax for defining variables. There are no groups and no levels, just elementary variables. There are, however, a number of optional parts. In its simplest form, a variable declaration in Java looks like this:

type name;

The data type comes first, and is followed by a name of your choosing. The statement ends with a semicolon, as do all Java statements. Here is an example:

int myVariable;

This defines a variable named myVariable, of type int (integer). It is really that simple. The valid types are the eight Java reserved words for built-in primitive data types, or class names defined in chapter 2. The eight primitives are shown in Table 5.2. These are the valid types for variables that are a primitive type. Note that for object reference variables, the type is the name of a Java class or interface (described in chapter 9).

To initialize a variable at the time you declare it, you simply equate it to a value, as in the following:

int myVariable = 10;

The value to which the variable is initialized can be either a literal or a variable that is already defined, or it can be an expression (for example, myVar + 2). In either case, the resulting type must be the same or compatible with the defined type of the variable.

There are some important differences between defining data items in COBOL and variables in Java:

  • Area A and Area B. Java is free-form.

  • Name length. There is no limit to names in Java.

  • Data length. You do not specify the digit's length for your variables in Java. You do specify a type, though, which predefines the number of bytes reserved in memory.

  • Levels. All variables are elementary. There are no groups, levels, conditions, or renames.

  • Clauses. There are no clauses in Java. However, there are modifiers, primitive types, and initialization syntax.

  • Location. With COBOL, your data items must be declared at the top of the program in the DATA DIVISION. In Java, class-level variables can be declared before or after the methods (we prefer before, others prefer after), while method-level variables can be declared anywhere in the code, as long as they are declared before use.

  • Capitalization. In Java, you do have to worry about case; abc and ABC are different. In COBOL, case doesn't matter.

What about groups? How can you live without them? Well, a group is a way to collect multiple data items into a single entity that can be manipulated as a unit or via the individual elements. That is what a class is in Java, so you group variables in Java by putting them in a class. All the instance variables are subordinate to that class.

To have deeper nesting, you can put some variables into another class, and include an instance of that class in the first class. You can nest class instances as deep as you wish—an object in one class may itself have objects, which in turn may have other objects, and so on. You can then add methods that operate on the variables in the group. Yes, you can assume that roughly every group data item in your COBOL program will become a separate class in Java, and all the paragraphs that do work on that group item will become methods in that separate class.

What about level-66 and level-88 data items? Not there at all in Java, sorry. Level 88 is somewhat handled by the boolean data type in Java, but that is not anywhere near as powerful as condition names in COBOL. Renaming is not in Java at all.

Variable names in Java are as long as necessary to be descriptive. Lowercase is used, except for the first letter of words other than the first word, such as interestRate or variablePayRate. This is the same convention for method names, while for class names, the first letter should be uppercase. Of course, we also use long names in COBOL, but the convention there is to use a hyphen to separate words, such as Interest-Rate. Remember that case is ignored in COBOL while it is not in Java.

Java variables can also be defined with optional modifiers before the data type, as in the following:

private int myVariable = 10;

In this case, the modifier private indicates this variable is not accessible (either to reference or modify) by code outside of this class. The list of modifiers is shown in Table 5.4.

Note

Modifiers are not allowed on local variables, only on instance or class variables.

Table 5.4: Java Modifiers.
Modifiers | Description
public, private, or protected | Refers to accessibility of the variable: public means all can access, private means only this class, and protected means this class or those that extend it. (See chapter 9.) The default is package, meaning this class or others in this package can access it. There is no language modifier keyword for this default, however.
static | This variable is initialized only once and has only one value, regardless of the number of instances of this class. It can be accessed via classname.variable, versus object.variable. Static variables are known as class variables, as there is one value per class, not one per object.
static final | This variable is a constant, and cannot be changed. By some conventions, constants always have uppercase names to distinguish them from non-constants. They are initialized when declared.

Notice that constants are defined in Java by coding both static and final modifers (and so constants are only valid at the class level). The static modifier saves memory, since constants only have one value. Use final to enforce this constancy; the compiler will flag any attempts to change a final variable.

The static and static final variables can also be defined as public, private, or protected. For classes that are not extendible (discussed in chapter 9), you should always define your non-static and non-constant variables as private, which will force users of your class to go through your methods. Chapter 9 shows that, for extendible classes, you should use protected versus private. Note that for some reason, it is considered acceptable to specify your static variables as public.

There are two other rarely used modifiers: transient and volatile. The former deals with the persistence of a variable when you serialize the class (discussed in chapter 14), while the latter deals with the behavior of a variable in a multithreaded application (discussed in chapter 11).


Variable Scope, Lifetime, And External Access

The discussion of data types leads to a discussion of variables. This in turn leads to a discussion of variable scope (which code inside this program has access to the variable), lifetime (when is the variable allocated memory, initialized, and freed) and external accessibility (what code outside of this program can access the variable). The following sections discuss these three aspects of Java variables, one at a time.

Variable Scope

In COBOL, all data items declared in the DATA DIVISION are local to the program in which they are declared. By default, nested programs do not have access to the data items of the parent program. This can be changed though by specifying the GLOBAL clause, which makes the data item accessible to all nested programs, as long as the nested program does not define its own data item with the same name.

The data items declared in a nested program are not available to the parent program. This is one of the benefits of nested programs—they allow you to encapsulate data items to that nested program, and not clutter the name space of the parent program. In this sense, nested programs are quite similar to methods in Java.

In Java, variables can be declared at the class level or at the method level. Basically, method-level, or local, variables are only accessible to the code contained in that method, while class-level variables are accessible to code in any of the methods in that class.

Within a method, you can also have parameter variables. These resemble locally declared variables except that they are passed by the caller rather than being defined within the code or body of the method. Otherwise, they are just like local variables. Note that the variable name used by the caller need not match the names defined for the parameters, just as with variables passed in COBOL using the CALL statement.

You can roughly equate outer-program WORKING-STORAGE or LOCAL-STORAGE (OS/390 only) data items in COBOL programs to class-level variables in Java, and DATA DIVISION data items in nested COBOL programs to local variables in Java methods. Furthermore, since COBOL programs support the passing in of parameters, you can equate LINKAGE SECTION data items to Java method parameters. (By equate, in this context, we are talking about the scope or direct accessibility of the variables.)

Moving further beyond parameters, Java has something called blocks, which are one or more statements contained inside curly braces. You have seen these already in relation to class definitions and method definitions, which are special cases of blocks. You can also have blocks defined directly inside methods, with their own variables local to that block, for example:

int myVariable;
myVariable = 10;
{
   int mySecondVariable;
   mySecondVariable = 20;
}
System.out.println("mySecondVariable =  " + mySecondVariable);

In this example, the variable mySecondVariable inside the braces or block has its own scope. This means it is not accessible to the code outside of that block, so you will get an error, since the println method call is trying to access an inaccessible variable. Notice that Java does not allow variable-hiding in blocks—that is, you cannot redefine myVariable from the outer block because this could lead to subtle programming errors.

Blocks like this are simply to aid program readability. There are other examples of blocks, such as the for statement, and you can declare variables in them that are local to that statement block, as in the following:

for (int index = 0; index < 10; index++)
{
   System.out.println("index = " + index);
}

Any attempt to reference the index variable after the last curly brace of the for statement will result in an error. COBOL has no equivalent to Java's local blocks.

The general scoping rule in Java, then, is that code can only directly access any variable in the current scope (block) or higher, up to the class level. (By directly, we mean without qualification.)

Variable Lifetime

When do variables come to life (get allocated storage), and how long do they live? For COBOL, as you know, WORKING-STORAGE data items come to life when the program is first called, and they live until the run unit is ended or the program is cancelled (for non-EXTERNAL data items).

If you specify EXTERNAL, then the data item is allocated when the run unit first starts, and persists as long as the run unit persists. For the lucky OS/390 programmers, you also have LOCAL-STORAGE data items, which come to life when a program is called and die when the program ends, automatically. Also, for all COBOL programmers, you can specify INITIAL, which causes VALUE clauses for WORKING-STORAGE data items to be executed on each call.

In Java, for local method variables and parameters, and variables inside nested blocks inside methods, these automatic or local variables come to life when the method is called or the block is entered. If an initialization has been specified, it is applied at that time. When the block ends or the method ends, the variable goes away.

For Java class-level variables, life is more interesting. Non-static class-level variables (that is, instance variables) come to life when an instance of the class (object) is instantiated with the new operator. Each class instance gets its own memory allocation for the instance variables, so they are totally independent of each other. If your instance variable declaration includes an initializer (for example, = 10), the initial value is applied at the same time (when the object is created).

For static class-level variables (that is, class variables), there is only one allocated memory location, regardless of the number of instances of the class. Since the life and values of a static variable are not dependent on—or even related to—instances of the class, it stands to reason that static variables come to life or are allocated memory as soon as the Java runtime knows about the class. This typically occurs when the entire Java application first starts, as it will find, verify, and load all classes used by the application. It might also be done when the class is explicitly loaded into memory by one of the various means of doing so. We haven't discussed this forced loading, but it is possible, using the forName static method in the Class class of the java.lang package. The important thing to remember is that static variables are alive as soon as you could possibly need them.

Variable External Access

The scoping of a variable refers to what code has direct access to it; in this case, we mean code in the same compilation unit as the variable. In COBOL, the compilation unit is the COBOL program. In Java, it is the class. In both COBOL and Java, it is possible for code outside of a variable's compilation to access that variable.

In COBOL, this is accomplished by specifying the EXTERNAL clause on the data item declaration. This allows other programs in the run unit to access this data item, providing they define it as EXTERNAL as well. This tells the compiler that this data item is allocated elsewhere, but that you intend to access it in this program.

In Java, the equivalent to the EXTERNAL clause from this accessibility viewpoint is the use of the public modifier on your class-level variables. This tells the compiler that you want any code in any other classes to have access to this variable. However, good form dictates that you will rarely, if ever, do this for non-static variables, as you should prefer to force users to go through your getXXX and setXXX methods to access your instance variables.

To prevent such global access to your variables, you can instead not specify any modifier, giving your variable package access. This allows only other classes in this package access to your class. To completely restrict other classes, you use the private modifier, indicating that only your class code has access. There is also a protected mode, limiting access to this class or other classes that extend or subclass this class—a topic discussed in chapter 9.

Assuming that you have made a variable available to outside classes, that outside code cannot simply use the variable name as-is. Whereas COBOL forces the external code to declare the data item again and mark it as EXTERNAL, Java forces the external code to qualify the variable name. For static variables, the variable name must be qualified with the class name separated by a dot (a period). For non-static variables, the variable name must be qualified with the class instance or object name, again separated by a dot. This dot operator tells the compiler that the variable is not part of this class, and further, tells the compiler (and the runtime) the class or class instance in which the variable can be found. The class itself is found by looking first in the current package, and then by searching the imported packages. Alternatively, the class name can be explicitly qualified with the package name.

Java Variables: An Example

At this point, it would be helpful, no doubt, to see an example of Java variables in action. Consider the example in Listing 5.1.

Listing 5.1: An example of Java variables.
Start example
public class Widget
{
   public static final int TYPE1 = 1; // constant
   public static final int TYPE2 = 2; // constant
   public static       int nextID= 0; // class
   private             int id;        // instance
   private             int type = 0;  // instance

   public Widget() // Constructor. Note name == class name
   {
      id = nextID; // references instance and class vars
      nextID = nextID + 1;
   }
   public boolean setType(int newType)
   {
      boolean inputOK = true; // local
      if (newType >= TYPE1 && newType <= TYPE2)
         type = newType; // references instance variable
      else
         inputOK = false;
      return inputOK;
   }
   public String toString()
   {
      String retString; // local
      retString = "Type = " + type + ", ID = " + id;
      return retString;
   }
} // end Widget class
End example

This example class has two constant variables ( TYPE1 and TYPE2), one static variable (nextID) and two instance variables (id and type). It also has a constructor method (Widget) and two other methods (setType and toString). All of these items access one or more of the class-level variables. The last two methods also declare their own local variables (inputOK and retString), while setType accepts a method parameter (newType).

Listing 5.2 shows the class in use.

Listing 5.2: Accessing class-level variables.
Start example
public class TestWidget
{
   public static void main(String args[])
   {
      Widget.nextID = 1000; // set class variable
      Widget myWidget = new Widget(); // object 1
      myWidget.setType(Widget.TYPE1); // call method
      Widget myWidget2 = new Widget(); // object 2
      myWidget2.setType(Widget.TYPE2); // call method
      System.out.println(myWidget); // calls toString method
      System.out.println(myWidget2); // calls toString method
   }
} // end TestWidget class
End example

Compiling and running this test class gives the following output:

>JAVA TestWidget
 Type = 1, ID = 1000
 Type = 2, ID = 1001

First, notice the toString method. If you supply this special method in your class, Java will implicitly call it when you specify an instance of your class on the System. out.println method, which we do in the TestWidget class. This method simply creates a String object that displays some interesting variable values. Including a toString method in your class is a good idea to make debugging easier, if nothing else.

This Widget example highlights a few of the items we have been discussing. It represents an item that you will want to reuse. Each unique Widget is identified by a type (we use the constants TYPE1 or TYPE2 to make this less error-prone) and by a unique integer identifier. The assignment of a unique integer identifier is done by using a static variable (nextID). You simply use and increment this variable in our constructor, so each Widget instance gets a unique value.

The problem is the need to seed this static variable at the beginning of the program, outside of the class. That is why it was made public, and you can see in the TestWidget class that it was seeded to 1000. The type is set per Widget by calling the method setType and passing in one of the defined constants for a value. Notice that the instance variable type is set to this value (if it is valid). Because id and type are instance variables, each Widget instance gets to maintain its own independent values for these, which is what you want.

Also notice that these instance variables were made private to avoid the risk of external code (outside of this class) directly changing any of Widget's values.

Take note in TestWidget how to qualify the references to the static variables in the Widget class by qualifying them with Widget. Without this, Java would look for these variables in the TestWidget class. Also note how to instantiate two instances of Widget by using the new operator, and then invoke the setType method on each one in order to independently change the instance variables of each object.

While COBOL's EXTERNAL WORKING-STORAGE data items are directly equivalent to class-level static variables, the concept of instance variables that have different values per class instance is foreign to COBOL. In essence, COBOL gives you exactly one instance of each program per run unit. This is true unless you are using OO COBOL (covered in appendix C), which does have classes and objects, and hence instance variables.

Something to watch for in Java versus COBOL is variable initialization. In COBOL, the compiler supplies reasonable defaults for data items. Java has a similar supplied default value for class-level variables, but not for local variables. Rather, a local variable must be initialized or assigned a value before it is first referenced, or the compiler will issue an error message. A good rule is to always initialize a variable explicitly, even if it is to zero. Even in COBOL!


Literals By Data Type

You have already seen an example of an int variable initialization in Java, so you are familiar with the syntax:

int myVariable = 10;

Let's round out this example with examples of initialization for each data type. It is important to know this, because any literal value you specify at initialization time or use later in an expression must be consistent with the target variable's data type. This is one of the aspects of a strongly typed language. Each data type, then, both in COBOL and Java, has an explicit syntax for literal values. We humans learn by example, so we will show examples of the syntax of literals in Table 5.5.

Table 5.5: Example Literals.
COBOL Type | Example | Java Type | Example
numeric, no decimals | 1234 or –1234 | byte, short, int, long | 1234 or –1234 or 1234L (l or L for long)
numeric, decimals | 1234.56 or –1234.56 | BigDecimal class | Not a primitive, so no literals
nonnumeric [1] | "a" or 'a' or QUOTE | char | .'a' or '\'' (for a quote)
nonnumeric n | "abc" or 'abc' (with *APOST) or 'Bob''s | String | "ABC" or "c:\\mydir" or "Bob\'s Store" store'
hexadecimal literals | X"0FF1F2OE" where every two characters is an EBCDIC hex character | byte, short, int, long, char | 0x1228 where every two characters is an ASCII hex character
DBCS | G'''' or N"<k1k2>" (< is shift-out, > is, shift-in) | String | "K1K2" where Kn is a DBCS character
float COMP-1 | 1.0E1 or 3.14E1 or ––3.14E1 or –3.14E-1 | float | 1e1f or 3.14f or –3.14f or –3.14e-1f (always ends in f or F)
float COMP-2 | 1.0E1 or 3.14E1 or –3.14E1 or –3.14E-1 | double | 1e1 or 3.14 or 3.14d or –3.14d or –3.14e-1d (optionally ends in d or D)
pointer | NULL or NULLs or ADDRESS OF | object reference | null or new MyClass()
procedure pointer | NULL or NULLs or "MYPROGRAM" | n/a |
boolean[1] | B'0' or B'1' | boolean | true or false
date | 19990930 |  | See chapter 8.
time[1] | 113000 |  | See chapter 8.
timestamp[1] | 19990930113000 |  | See chapter 8.
[1]OS/400 only.

The character data type takes a literal enclosed in single quotes. In Java, this can be only one character, while in COBOL it can be multiple characters. COBOL allows EBCDIC hexadecimal characters if they begin with the letter x, while Java allows ASCII hexadecimal numbers if they begin with 0x or 0X. Java also allows special character values if they are preceded by a slash, which is called an escape sequence. Examples are shown in Table 5.6.

Table 5.6: Java Escape-Character Sequences.
Escape Sequence | Description
\n | newline character
\t | tab
\b | backspace
\r | carriage return
\f | form feed
\\ | backslash
\' | apostrophe
\" | quote
\ddd | octal number, not to exceed 377
\uxxxx | Unicode number; must be 4 digits

The non-float numeric values in Java are simple integers that must not be larger than the capacity of the data type. Long data type literals do allow an optional l or L character after them (this character can be upper- or lowercase). This tells the compiler that these are long values, which can be important in expressions, where promotion between data types can happen implicitly. If the L is not specified, all integer literals are assumed to be of type integer (four bytes) by Java. By default, these literals are assumed to be base-10 decimal numbers, but you can also specify octal (base-8) or hexadecimal (base-16). For octal, use a leading zero, as in 035. For hexadecimal, use a leading 0x or 0X, as in 0x1d.

Float-type literals in COBOL are of the form <+/->mantissa E<+/->exponent, where the angle brackets denote an optional part. In COBOL, the mantissa must have a decimal point, and the E is required.

Float-type literals in Java are decimal numbers, followed by an optional decimal point, followed by an exponent. The literal can be followed by an F for single-precision float or D for double-precision float—the default is double. The exponent part consists of an E or e, followed by a positive or negative decimal number. If no F or D is specified, then any literal with a decimal point is assumed to be double-precision. Unlike COBOL, you can also force an integer literal to be a float literal by simply appending an F or D after the literal, as in 123F.

Figurative Constants

COBOL also allows the use of the figurative constants SPACE or SPACES to initialize a character string to all blanks, ZERO, ZEROS, or ZEROES to initialize a numeric data item to zero, and NULL or NULLS to initialize a pointer to empty. Also, to embed a quotation mark inside a string literal, you can use the QUOTE or QUOTES figurative constant. Further, COBOL has the handy little figurative constant ALL for initializing all positions of a data item to the given literal.

Java does not have any figurative constants for strings or numbers. It does have null for object reference pointers and true and false for Boolean data type variables. It also, as discussed, uses escape characters in character or string literals to specify characters that would otherwise not be typable or cause the compiler confusion, such as backslash plus quote (\') for a quote or backslash plus n for a new line (\n).

There is no equivalent to COBOL's ALL in Java, but keep in mind that assigning zero to a numeric variable sets all bytes in storage for that variable to zero. Also, strings in Java are always variable length, not fixed length as they can be in COBOL. So, assigning a literal like "ABC" to a String data type variable sets the length to three and the contents to A, B, and C. If the previous contents were larger, the length is truncated. If they were smaller, the length is automatically extended. You can always query the length with the length method of the String class (covered in chapter 7).


Numeric Data Type Ranges

When programming with numeric data types, it is important to know the limits to each type, so that you can choose the appropriate one to avoid overflow or truncation.

In COBOL, when you define a numeric data item and use the PICTURE clause to set an integral length and decimal length, you do so by specifying how many decimal digits the data item will hold. You do not specify how many bytes of memory to allocate; that is done implicitly by the compiler, based on the number of digits that you indicate. In Java you specify a data type, but no length. Rather, Java supplies a number of different numeric types for you, each representing a different, predefined length or capacity. This implies that you know what the limit is for each data type, in order to choose the correct one for your particular case. You could simply always choose the largest—long for integers and double for floating-point—but these large variables might take up more space than necessary.

In order to determine the limits for each data type, you have to know the underlying size of each in bytes. Thus, in the end, you are forced to think in terms of numerical limits, rather than the number of digits as you usually do in COBOL. Let's review the COBOL category ranges. The bytes required in memory depend on a combination of the PICTURE clause (maximum digits) and the USAGE clause (internal storage representation), as shown in Table 5.7.

Table 5.7: COBOL Memory Requirements by Data Type.
Type | Bytes | Range
numeric zoned (USAGE DISPLAY) | number of digits | up to 18 digits
numeric binary (USAGE BINARY or COMP) | 4 or less digits = 2 bytes, 5 to 9 digits = 4 bytes, 10 to 18 digits = 8 bytes | up to 18 digitis
numeric packed (USAGE PACKED-DECIMAL or COMP-3) | (number of digits / 2) + 1 | up to 18 digits (OS/390) or 31 digits (OS/400)
float USAGE COMP-1 | 4 | 1.175 494 4 E-38 to 3.402 823 5 E+38
float USAGE COMP-2 | 8 | 2.225 073 858 507 201 E-308 to

Table 5.8 compares these ranges to those of Java's built-in numeric types.

Table 5.8: Java Memory Requirements by Data Type.
Type | Bytes | Range
byte | 1 | 27 = -128 to 127
short | 2 | 215 = -32,768 to 32,767
int | 4 | 231 = -2,147,483,648 to 2,147,483,647
long | 8 | 263 = 9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
float | 4 | 1.402 398 64 E-45 to 3.402 823 47 E+38
double | 8 | 4.940 656 458 214 465 44 E-324 to 1.797 693 134 862 315 70 E+308
Note

The ranges given for float and double in both languages are positive numbers. The negative number range is the same.

Float Versus Decimal

What are floating-point variables, and why would you use them? You can see that for Java, float and double are the only primitive data types that support decimal points. Since your COBOL code probably has decimal data items, your first inclination might be to use float or double in your Java code in their place, and to use int or long for zero-decimal numbers.

However, floating-point variables (whether single- or double-precision) are somewhat less efficient than decimal data items because floating-point variables are designed to hold arbitrary values, without benefit of previous knowledge about the number of decimals. That is, you do not tell the compiler at definition time how many decimals the data will contain; you simply specify whether it is to be four bytes or eight bytes long. Any valid floating-point number can be assigned to that variable during runtime.

This makes for great flexibility, but it also means that the internal storage of these numbers has to be flexible. As always with flexibility, there is a performance cost in order to interpret the data at runtime—the internal format of the data essentially must be converted every time it is referenced. Compare this to decimal data items in COBOL, where you tell the compiler exactly how many decimals the data item has. This allows the compiler to store the data in a fixed format every time, and allows mathematical operations to exploit this knowledge for very fast evaluations.

Therefore, in cases where you are dealing with a predetermined number of decimals (such as with amounts of money), floating-point is not a good replacement for COBOL's decimal data items. Floating-point data items are a good choice when dealing with data for which you cannot predict the exact decimal precision, such as with scientific data, real-world measurements like the size of a human hair, and so on. They are also of value when the number of decimals can be hard-coded, but the size of the number cannot. For example, if the number may become extremely huge or extremely small (well below one), float or double can handle it. It is hard to imagine what sort of data would need to allow for numbers bigger than 1.7 * 10308!

For business applications that deal with pay rates, unit costs, unit prices, and so on, it is hard to imagine the need for the power of floating-point numbers, with their attendant costs. It is too bad the designers of Java did not see fit to support a built-in decimal data type, but clearly they were not coming from a business background.

In Java, the only primitive data types that support decimals are float and double, so it appears you are stuck with these. Unfortunately, they have other, perhaps more serious, drawbacks than performance—namely, their propensity to produce high-precision results after a mathematical operation. You do not want to bill your customers in ten-thousandths of a cent, for instance!

Let's look at an example in Java. Imagine you have widgets to sell for $1,234.56, and you decide to have a sale where you are offering 23 percent off. What might the calculations for the new price look like? Consider Listing 5.3.

Listing 5.3: Java calculations for pricing.
Start example
public class TestFloat
{
   public static void main(String args[])
   {
      double unitCost = 1234.56;
      double discount = 0.23;
      System.out.println("unitCost = " + unitCost);
      unitCost = unitCost - unitCost * discount;
      System.out.println("saleCost = " + unitCost);
   }
}
End example

The result is the following:

>java TestFloat
unitCost = 1234.56
saleCost = 950.6111999999999

Can you imagine sending this bill to your customers? To circumvent this problem, you might try writing your own complicated code to force the decimal precision and make some decision on rounding, but this would be ugly code.

All is not lost, however. The Java designers did supply, at least, a Java class to emulate the mathematical behavior of a decimal data item. While not a built-in type, it is at least some- thing that can offer accurate results with methods for easily setting the decimal precision. The class is called BigDecimal, and you find it in the java.math package. This class offers you complete control over rounding behavior on divide and decimal-point adjustments, and offers a very complete set of mathematical and conversion functions in the form of methods. We recommend that you use this class. The Java-standard JDBC database access package (discussed in chapter 13) maps decimal database variables to this class.

To understand this class and its documentation in the JDK, you have to be familiar with its terminology and architecture. An instance of a BigDecimal class is really the following:

  • An integer value like 123456, with no limit on the total number of digits (called the precision).

  • A decimal position (the scale) within that integer value. This is specified in terms of the number of digits to the right of the decimal point.

When you create a new BigDecimal object, you have three choices for the initial value: a string like 1234.56, a BigInteger object (another class in java.math), or a double value. If you get your value from the user, it will probably be as a string. Using a string sets both the integer value (123456) and the scale (two, in this example, since there are two decimal positions).

Alternatively, you can use the static method valueOf(long val, int scale) to return a new BigDecimal object with the integer value and scale you specify:

BigDecimal unitCost = BigDecimal.valueOf(123456, 2);

Once you have a BigDecimal object, you can perform all the usual mathematical operations on it via methods like add, subtract, divide, multiply, abs, and negate. You can also use comparative methods like compareTo, min, and max to compare it to another BigDecimal value. (Note that compareTo ignores trailing zeros in the decimal portion, so 1234.00 is considered equal to 1234.0000. If you do not want this, use the equals method.) There are also methods for converting a BigDecimal object to other data types such as integer (intValue), long (longValue), double (doubleValue), BigInteger (toBigInteger), and, of course, string (toString).

How does BigDecimal deal with the growing precision problem as seen in the example involving the 23 percent discount? By default, it gives you more decimal points after a multiplication than you started with. (Specifically, it gives you back the sum of the decimal points of the two operators.) It also supplies a very useful method called setScale to set the number of decimal points after an operation like multiplication. This method simply moves the decimal point, but leaves the nondecimal part of the number unchanged. For example, if you start with 1234.5678 and call setScale(5), you get 1234.56780.

But what if you call setScale(2)? This gives you back either 1234.56 or 1234.57, depending on the rounding behavior you ask for. This behavior can be specified as the second parameter to setScale, and must be one of the eight constants defined in BigDecimal. You usually only use two of these—either BigDecimal.ROUND_DOWN (which gives you 1234.56, i.e., a simple truncate) or BigDecimal.ROUND_HALF_UP (which gives you 1234.57, i.e., rounds up if truncated digits are greater than five). Let's revisit our example, then, this time using BigDecimal instead of double, as shown in Listing 5.4.

Listing 5.4: Using the BigDecimal class for rounding.
Start example
import java.math.*;
public class TestBD
{
   public static void main(String args[])
   {
      BigDecimal unitCost = new BigDecimal("1234.56");
      BigDecimal discount = new BigDecimal(".23");
      System.out.println("unitCost = " + unitCost);
      // unitCost = unitCost – unitCost * disCount
      unitCost = unitCost.subtract( unitCost.multiply(discount) );
      System.out.println("unitCost after discount = " + unitCost);
      unitCost = unitCost.setScale(2, BigDecimal.ROUND_HALF_UP);
      System.out.println("unitCost after setScale = " + unitCost);
   }
}
End example

This gives the following result:

>java TestBD
unitCost = 1234.56
unitCost after discount = 950.6112
unitCost after setScale = 950.61

There are only two methods in BigDecimal that might cause truncation of data from the decimal part of the number: setScale and divide. All other operations increase the number of decimal digits if required. Note that the nondecimal part of the number is never truncated; it simply grows as much as necessary to hold the result. For setScale and divide, you should choose BigDecimal.ROUND_HALF_UP if you normally use ROUNDED in COBOL; otherwise, you should choose BigDecimal.ROUND_DOWN. There are a total of six additional rounding options, as well: ROUND_CEILING, ROUND_FLOOR, ROUND_HALF_DOWN,

ROUND_HALF_EVEN, ROUND_UNNECESSARY, and ROUND_UP.

Note in Listing 5.4 how a BigDecimal method call is nested as a parameter to another BigDecimal method call:

unitCost = unitCost.subtract( unitCost.multiply(discount) );

This is possible because the multiply method returns a new BigDecimal object, which is then passed in as a parameter to the subtract method. This method also returns a new BigDecimal object, which is equated back to the original unitCost object reference variable. All the math methods return new objects rather than changing the object on which they are invoked. That is why you must code

object = object.method(xxx)

when using the BigDecimal class. It is read-only, or in Java-speak, immutable. No method has any impact on the object on which it is directly invoked, rather, the method returns a new object altogether. If the returned object is equated to the source object variable, then the original object in memory is lost and will eventually be swept up by the garbage collector. (Another immutable class, String, is discussed in chapter 7.)


Boolean Data Type

Since the boolean data type in Java is somewhat foreign to COBOL programmers, we believe that it is worth a brief description. In both COBOL and Java, you have expressions that evaluate to true or false and so affect the flow of control in your program, such as the following:

if (amountDue > 0)
   // do something

The notion of true or false is intrinsic to computing, and is the result of any conditional expression, no matter how complex. The Java boolean data type captures this notion, and the special keywords true and false are used to indicate state. You can initialize and set Boolean variables like this:

boolean myFlag = true;
myFlag = false;

Boolean variables can also be used directly, as expressions. For example, all of the fol- lowing are valid:

if (myFlag == true)
   //
if (myFlag)
   // ...
if (myFlag == false)
   // ...
if (!myFlag)
   //

This is quite handy. Boolean variables can be used wherever an expression or sub-expression is allowed, including while and for loop conditionals. Most interestingly, Boolean variables can be assigned an expression! That is, they can be assigned the result- ing true or false value of an expression, as in the following:

myFlag = (amountDue > 0); // will be true or false

This can make for elegant and compact code. Here is another little example to show how to convert between integer and Boolean values:

// boolean test;
int y = 0;
if (test)
   y = 1;           // Boolean to integer
y = (test) ? 1 : 0; // Boolean to integer (option 2)
test = (y!=0);      // Integer to boolean


Casting And Numeric Conversions

Casting is the process of converting a variable's value from one data type to another. CO- BOL's way of converting numeric data items from one category to another is very straightforward—you simply execute the MOVE or COMPUTE statements, and COBOL magically maps from the source type to the target type. If the resulting data item is bigger than the source, the result is zero-padded on the left for the integer part, and on the right for the decimal part. What happens if the value being moved will not fit in the result? It is truncated, that's all. Using COMPUTE, you can catch this problem by specifying the ON SIZE ERROR phrase.

Java is somewhat similar to COBOL in this respect. That is, in most cases, you can simply move data between data types with no unique syntax required, other than an assignment statement:

short myShort = 10;
int   myInt = 20;
long  myLong;
myLong = myShort;
myLong = myInt;

This is a perfectly legal and common practice. Notice, however, that only safe conversions are being done here, from a smaller type to a larger type. Thus, the result will always safely fit in the target variable. What if the target type is smaller than the source type? Consider this example:

long myLong = 40000;
short myShort = myLong;

This will fail. It will not even compile, as opposed to COBOL, where it would be legal at compile time, and at runtime the result would simply be truncated if necessary.

Sometimes, however, you might want to do this because you know that the resulting value will fit in the target. There is a way to force Java to allow the conversion and simply truncate the result if necessary. (But, of course, you should avoid these situations!) It involves some simple syntax called casting, which looks like this:

long myLong = 20000;
short myShort = (short)myLong;

The syntax specifies the resulting type in parentheses before the variable or operation:

(target type) source-value

This is simply a directive to the compiler that you know what you are doing and you will take responsibility for the risk of truncation. Casting like this is required for any conversion from one data type to another of lower precision. It is allowed in all other cases as well, but not required.

Table 5.9 summarizes where casting is necessary in all the numeric type-to-type conversions. Read this table by scanning across the rows, left to right. You see that converting from a data type specified in the left-hand column to any data type in the columns across the top does not require a cast for those target data types that have higher precision.

Table 5.9: Java Casting Requirements by Data Type. (Read left to right.)
 | To | byte | char | short | int | long | float | double
From |  |  |  |  |  |  |  |
byte |  | Cast |  |  |  |  |  |
char | Cast |  | Cast |  |  |  |  |
short | Cast | Cast |  |  |  |  |  |
int | Cast | Cast | Cast |  |  |  |  |
long | Cast | Cast | Cast | Cast |  |  |  |
float | Cast | Cast | Cast | Cast | Cast |  |  |
double | Cast | Cast | Cast | Cast | Cast | Cast |  |

There are only two apparent anomalies in Table 5.9: a cast is required from byte to char and from char to short . Since byte is one byte long, char is two bytes long, and short is also two bytes long, you would not expect to have to cast. Both anomalies occur because char is unsigned, while both byte and short are signed. Moving data from a signed to an unsigned data type might cause a loss or misinterpretation of the sign for negative numbers, and from unsigned to signed might cause loss of precision.

Note

If you are using a BigDecimal object, you cannot use this primitive type-casting technique. Rather, use the supplied xxxValue()method in the BigDecimal class, where xxx is one of the numeric primitive types, for example, double myDouble = myBigDecimalObject.doubleValue();

What about operations other than simple assignment, such as arithmetic expressions? With the MOVE statement, you do not have to tell the COBOL compiler of your intentions for data item conversions. The compiler does the conversion and expression evaluation internally before placing the result into the target data item.

Suppose you want to add int, short, and byte variable values together in Java, divide the result by a floating-point number, and perhaps even assign the result of the expression to a double variable. Listing 5.5 shows an example of this.

Listing 5.5: Using multiple types of numbers.
Start example
public class TestEXPR
{
   public static void main(String args[])
   {
      byte   byt  = 30;
      short  sht  = 20;
      int    int1 = 20;
      double dbl  = 10.0d;
      float  flt = (float)(((byt+sht-int1)*2.0)/dbl);
      System.out.println(dbl);
   }
}
End example

In Listing 5.5, different numeric fields are declared. The highlighted line defines the final expression. It consists of adding the byte and short variables and then subtracting from the result an int variable. After multiplying by two, the result is divided by a double variable. The most important thing in this example is that the final result is cast down to a float. The result of executing this is the value 6, which is the result of the operation.

Behind the scenes, a series of rules is used to convert the intermediate results to a common format, usually that of the highest precision of all the operands:

  • If at least one of the operands is of type double, the intermediate result is double, and eight-byte math is used. (If the other operand is not also double, it is implicitly cast to double.)

  • If at least one of the operands is of type float, the intermediate result is float, and four-byte math is used. (If the other operand is not also float, it is implicitly cast to float.)

  • If at least one of the operands is of type long, the intermediate result is long, and eight-byte math is used. (If the other operand is not also long, it is implicitly cast to long.)

  • If none of the above is true, the intermediate result is int (all operands are implicitly cast to int), and four-byte math is used.

Casting is often done, both implicitly and explicitly, in Java. It is also done with parameters on a method call, and on the return statement of a method. For example, if you define a method's return type to be integer, your actual returned value can be a byte, character, short, or integer value without explicit casting. If you return a long value, you must explicitly cast it, as follows:

public int testThis()
{
   long retValue = 20000;
   return (int)retValue;
}


Java Data-Type Class Wrappers

Java supplies class wrappers for its primitive data types. These are classes in the java.lang package that allow you to work with each of the data types as objects instead of primitives. All of the operators are emulated via methods in the class. Furthermore, the classes supply some interesting and worthwhile additional methods and constants. The classes are: Boolean, Byte, Character, Double, Float, Integer, Long, and Short.

These classes come in handy when an object is needed, but you have a primitive. For example, you might have an array of mixed values—some integers and some objects. This is not legal, of course, so you would instead use the class wrappers to produce objects from your integer values. You would define the array to hold objects of class Object (a generic class discussed in chapter 9).

All of these classes contain constructors that accept a primitive type value as input for conversion to an object. Most importantly, they all supply methods to convert to and from a string, such as the methods parseInt and valueOf in the Integer class. Java only accepts user input in the forms of strings, making conversion essential, so you will use these classes often. Table 5.10 lists the methods supplied by each class to convert from a string, convert to a string, and convert to a primitive type.

Table 5.10: Java Primitive-Data-Type Class Wrappers.
Class | From a String | To a String | To a Primitive
Boolean | ValueOf(String) | toString() | booleanValue()
Byte | ValueOf(String) decode(String) | toString() | byteValue(), doubleValue(), floatValue(), intValue(), shortValue(), longValue()
Character |  | toString() | charValue(), getNumericValue()
Double | ValueOf(String) | toString() | doubleValue(), byteValue(), floatValue(), intValue(), longValue(), shortValue()
Float | ValueOf(String) | toString() | floatValue(), byteValue(), doubleValue(), intValue(), longValue(), shortValue
Integer | ValueOf(String) parseInt(String) | toString() toBinaryString(long) toHexString(long) toOctalString(long) | intValue(), byteValue(), doubleValue(), floatValue(), longValue(), shortValue()
Long | valueOf(String) parseLong(String) | toString() toBinaryString(long) toHexString(long) toOctalString(long) | longValue(), byteValue(), doubleValue(), floatValue(), intValue(), shortValue()
Short | valueOf(String) parseShort(String) | toString() | shortValue(), byteValue(), doubleValue(), floatValue(), intValue(), longValue()

Note that the valueOf(String) methods all throw the exception NumberFormat Exception if the input string is not valid. This is Java's equivalent to signaling a condition in LE/ILE, and you must monitor for these using try/catch blocks (covered in chapter 10). Listing 5.6 shows a program that accepts a float value as a string from the command line, then converts it to a primitive float using the Float class.

Listing 5.6: Using the Float class to convert a number from a character string to a primitive float.
Start example
public class TestConvertFloat
{
   public static void main(String args[])
   {
      Float floatObject;
      if (args.length != 1)
         return;
      try
      {
         floatObject = Float.valueOf(args[0]);
      }
      catch(NumberFormatException exc)
      {
         System.out.println("Invalid input!");
         return;
      }
      float floatValue = floatObject.floatValue();
      System.out.println("input = " + floatValue);
   }
}
End example

Note that args[0] represents the first parameter string passed in the command line. The result of compiling and running Listing 5.6 with various inputs is as follows:

>javac TestConvertFloat.java
>java  TestConvertFloat 1.2
input = 1.2
>java  TestConvertFloat 1.02
input = 1.02
>java  TestConvertFloat 0000.12
input = 0.12
>java  TestConvertFloat 1.2e4
input = 12000.0
>java  TestConvertFloat -1.2e4
input = -12000.0
>java  TestConvertFloat abcdef
Invalid input!

Another useful thing about these classes is that they hold constants for the minimum and maximum values these data types allow—for example, Integer.MAX_VALUE and Inte-ger.MIN_VALUE. Many other useful methods exist, including a number of isXXX() methods in the Character class, for example, isSpaceChar(). Many of these methods are static and take their target as a parameter, so they do not require an instance of the class. The class then becomes just a convenient place to group these related traditional-style functions. It is worth your time to peruse the JDK documentation pertaining to these classes.


Summary

This chapter discusses the data types and variables available in Java, as follows:

  • Java has two categories for data types: primitive types and reference types.

  • Java has eight primitive data types: boolean, char, byte, short, int, long, float, and double.

  • Java does not have equivalent data types to COBOL's various decimal data categories. However, the class java.math.BigDecimal works well for decimal data.

  • The character data type in Java represents one character only. Multiple character strings require the String class.

  • Character and string data in Java is Unicode based, versus COBOL's EBCDIC base. (Information on the Unicode standard can be found on the Web at www.unicode.org.)

  • Java variables are defined with <modifiers> type name;.

  • Java does not directly support structures. Rather, classes themselves are structures, which also happen to allow methods in them.

  • Only class-level variables can specify modifiers.

  • The modifiers for variables are public, private, protected, static, final, transient, and volatile.

  • Constants are defined in Java by specifying the modifiers static and final.

  • In Java, you can initialize a variable when it is declared, using the assignment operator (=) and a literal, variable, or expression. This compares to the VALUE clause in COBOL.

  • Without qualification, Java code can access local variables, parameters, or class-level variables of the containing class only.

  • With qualification, class-level variables in other classes can be accessed, depending on the modifiers specified for them.

  • Static variables have a single value per class, while non-static variables have a unique value per class instance or object.

  • Casting in Java is implicit when converting to a higher-precision data type, but explicit cast syntax is required when converting to a lower-precision data type.

  • Class wrappers for each of the primitive data types offer string conversion and other useful methods.


Chapter 6: Arrays,Vectors, And Hashtables

Overview

As discussed in earlier chapters, Java programs contain two types of variables:

  • Primitive types are defined to be one of the primitive data types or categories: boolean, byte, char, short, int, long, float, or double. These are similar to COBOL's data items.

  • Reference types are defined to be of a class type. The class can be either a Java-supplied class or a user-defined class.

Reference type variables can be declared as one of the following types:

  • Class type. This can include any of the classes that Java supplies in its java.xxxx packages, or one of your own classes. This can also be the Java-supplied String class described in chapter 7. Here are two examples:

    MyClass myVariable;
String myString;

  • Interface type. Interfaces are special kinds of classes that define only method names and parameters (signatures) without supplying the code for them. While the variable can be declared as an interface type, it can only be assigned to a class instance. Interfaces are discussed in chapter 9.

  • Array type. Arrays in Java are the equivalent of variable-occurrence data items or tables in COBOL. In Java, arrays have special syntax and, perhaps surprisingly, are actually objects. An array variable is an object reference variable. Array objects contain multiple occurrences of a primitive data type or of other objects. All occurrences or elements of a single array must be of the same type, and that type must be declared on the array object-reference variable declaration.

In this chapter, you learn about arrays in Java, and some of their "cousins," which offer similar but specialized functionality.

Note

There are no explicit pointers or pointer arithmetic in Java. This is unlike COBOL, where pointers and pointer arithmetic are supported. Java just contains reference type variables, which are like pointers in that they hold memory addresses. However, you cannot increment, decrement, or otherwise mathematically manipulate these memory addresses. They can only be assigned and compared. This gives you most of the benefits of pointers, but with added security, ease of use, and strong type checking. It does, however, require explicit language support for creating and manipulating arrays.


Arrays

As a seasoned programmer, you know you need a raise. Sorry—we mean arrays. Both COBOL and Java support arrays, but with some noticeable differences between the languages. For example, the terminology is different. COBOL uses the terms variable-occurrence data items or tables. Each data item in a table is known as an occurrence. The total number of occurrences is referred to as the size.

Java uses the term array, and each item in the array is referred to as an element. The total number of elements is the length. Table 6.1 compares the two languages in terms of their support for tables/arrays.

Table 6.1: COBOL Tables Versus Java Arrays.
COBOL | Java
Multiple dimensions (maximum seven). | Multiple dimensions (no limit).
Fixed in size at compile time, unless you use the OCCURS DEPENDING ON clause, which specifies the minimum and maximum sizes. | Fixed in length at run time, but cannot be changed once set. The Vector class in the java.util package can be used for truly dynamically sizable arrays.
Can initialize at declaration time. | Can initialize at declaration time.
Can initialize by assigning values in the code. | Can initialize by assigning values in the code.
Supports built-in search capability | No built-in search functions, except via the Arrays class in java.util (new in JDK 1.2), or via the Hashtable class, also in java.util.

As you can see, there is similar functionality in both languages, especially when the new java.util.Arrays class in JDK 1.2.0 or higher is used. This class is chock-full of static helper methods for searching and sorting arrays, and gives similar support to COBOL's SEARCH statement and much more. Further, the java.util.Vector class gives a big boost in Java, as you will see.

Java has explicit syntax for arrays, and you can use them for all of your purposes. However, there are also two other options at your disposal in Java: vectors for lists that can grow and shrink programmatically, and hashtables for key-plus-value pairs and efficient lookups. While arrays, vectors, and hashtables have been around since day 1 in Java, as of JDK 1.2.0, there are also a number of new classes in java.util for creating more advanced "collections" with specific performance and functional capabilities, including linked lists, maps, sets, and trees. These new classes are not covered here, as you will find the three original options sufficient for most of your needs. We encourage you, however, to look at the documentation for the new classes in JDK 1.2.x.

The length or size (number of elements or occurrences) of arrays in both languages must be defined once. In COBOL, you specify the size of your table in your source, and cannot change that size dynamically at runtime (except when using the OCCURS DEPENDING ON clause, which we won't discuss here, for simplicity). Java allows you to defer specifying the array length until later in your procedural code, with either a literal or an expression, versus hard-coding it at declaration time. This can be a big help when the total number of elements must be computed. However, once set, it cannot be changed.

Declaring Arrays

In Java, the syntax for declaring an array variable is essentially the same as declaring any variable, but with the added notation of square brackets to identify it as an array versus a simple scalar variable. Here is a simple example:

int oneDimArrayOfInts[];

This shows an array defined by specifying an element type (int, in this case) that all elements will have. You also specify a name for the array, and indicate it is an array by using square brackets. Note these square brackets can come after the name, or after the type, as follows:

int [ ] oneDimArrayOfInts;

There is no clear consensus on which is better, but we prefer to place the brackets after the name. Also, notice that some spaces are added before and inside the brackets. This is to highlight again that Java ignores white space.

How many dimensions are in this array? One. You know this because there is only one pair of brackets, regardless of where they are. What is the size of this array? There is no way to know yet!

Here is the tricky part of defining arrays compared to defining tables in COBOL: You have not really created an array at all—you have only created an object reference variable that will eventually point or refer to an array object. Notice that, although the type is primitive (int), it is an object reference variable because of the brackets. Hence, it is an array type. Actually, all array variables have a type that is a class you never see, a special Array class that Java supplies. The type you specify is not for the variable itself, but rather for all the elements in the array.

Here is another example:

long twoDimArrayOfLongs[][];

This example defines twoDimArrayOfLongs as a two-dimensional array variable that will contain elements of type long. You know it is two-dimensional because there are two pairs of square brackets, [ ] and [ ], and each pair indicates a dimension. That is the rule in Java: the number of bracket pairs is equivalent to the number of OCCURS clauses in COBOL.

Take a look at this example:

char[] twoDimArrayOfChars[];

Here, the twoDimArrayOfChars array variable is defined as a two-dimensional array of character type. Again, you can specify the square brackets after the name of the array or after the type of the array. Indeed, you can mix between the name and the type. Either way, remember that the total number of paired brackets determines the number of array dimensions.

We recommend not splitting up the pairs like this, however. If you want a standard, always put the brackets after the variable name, not the type, since it is the variable that is the array, and for some people, this is more intuitive. Note, though, that if the brackets follow the type versus the name, and you identify multiple names separated by commas, all the names become arrays. For example, this declares three arrays of integers:

int[] arrayOne, arrayTwo, arrayThree;

Compare that to the following, which declares only one array and two non-array "scalar" variables:

int arrayOne[], variableOne, variableTwo;

Creating or Instantiating Arrays

In Java, it is important to understand the difference between declaration and creation. We cannot overstate that in Java, arrays are objects. Not only must you declare them, you must also create them using the familiar new operator. Any attempt to reference declared array variables that are not yet created will result in a compile-time error.

You can define, create, and initialize an array in Java all in one step, in two steps, or in three steps. The following lines expand the previous examples:

int oneDimArrayOfInts[]     = new int[1000];
long twoDimArrayOfLongs[][] = new long[10][10];
char[] twoDimArrayOfChars[] = new char [20] [20];

The main difference here is the use of the new operator to create the arrays at declaration time. Notice the syntax of the new operator for arrays:

  • It requires the element type to be specified again, even for primitive types.

  • It uses square brackets, versus parentheses when instantiating regular classes.

  • It requires an integer number between the brackets, which is the number of elements this array will hold (the length, or the size).

In our example, oneDimArrayOfInts contains 1,000 elements, twoDimArrayOfLongs contains 10 elements in both dimensions, giving it 100 elements, and twoDimArrayOf Chars contains 20 elements in both dimensions, giving it 400 elements.

You do have a choice here. You can use the new operator at declaration time, or you can defer creating the array until later. It's your choice. For example, the following are equivalent:

int oneDimArrayOfInts[] = new int[1000]; // … or …
int oneDimArrayOfInts[];
oneDimArrayOfInts = new int[1000];

At this point, you have actual array objects whose elements can be assigned, referenced, and compared. You will see how to do this shortly.

In these examples, an integer literal was specified for the length of the array. In COBOL, you must use a literal (unless you are creating variable-size tables), but in Java, you can actually specify any expression that evaluates to an integer. COBOL determines the size of the table at compile time (even variable-size tables require literals for the minimum and maximum number of occurrences), while Java defers that calculation until runtime. Once the array is created, its length is fixed, but at least you can defer creation until you have done some calculations to determine how big to make it. Since you can declare it first and instantiate it later, even arrays declared at the class level can defer their creation (instantation, remember) until later, possibly deep inside a method, after determining how big the array should be.

Compile-Time Initialization of Arrays

Compile-time arrays are supported by both COBOL and Java. These are arrays in which the elements are initialized at the same time the array is declared. The syntax of initializing array elements as part of the declaration clearly differs between the two languages. In COBOL, with compile-time tables, you define the table inside a level-01 group item (say) that uses the VALUE clause to specify the initial data. You then define the actual table at a subordinate level with an OCCURS clause. This partitions the initial data into table entries, as shown:

01 ALLDAYS VALUE 'MONTUEWEDTHUFRISATSUN'.
  05 DAYS OCCURS 7 TIMES PIC X(3).

The OCCURS clause tells the compiler the number of occurrences in the table. The example shows a table of seven elements, producing the following:

DAYS (1) = 'MON'
DAYS (2) = 'TUE'
DAYS (3) = 'WED'
DAYS (4) = 'THU'
DAYS (5) = 'FRI'
DAYS (6) = 'SAT'
DAYS (7) = 'SUN'

Compare this to what Java offers in terms of compile-time arrays. Here is the same example in Java:

String days[] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};

In Java, character variables are only of length one, while the supplied String class is used for strings of multiple characters. Thus, this example uses an array of String objects. The more complex alternative is a two-dimensional array of characters. The syntax shown here is the special syntax Java supports for defining, creating, and initializing arrays in one step. In this case, the initial values are supplied between curly braces, with each element's initial value separated by commas. The literal values supplied must be consistent with the declared element type of the array. Also, don't forget the ending semicolon, as always. The spaces are not important, and whether this is on one line or spread over eight lines also is not important.

When you specify initial values within an array declaration in this way, Java performs the new operation and defines the array length for you. The number of comma-separated elements in the initialization part implicitly determines the array length. These types of compile-time arrays are often used to hold constant data, and so are often defined as final. Since they are unchanging, we can live with one instance per class versus per object, and so make them static, as well (as we do with all constants in Java):

static final String days[] = {"MON","TUE","WED","THU","FRI","SAT","SUN"};

Runtime Initialization of Arrays

With runtime arrays, you initialize the values of the elements in a separate step from declaring the array. Thus, as the name implies, runtime arrays are loaded (or initialized) at runtime, during program execution. Your program code initializes them by assigning values to the elements, typically in a loop that stops at the length of the array. Listing 6.1 is an example of how this is done in COBOL.

Listing 6.1: Initializing a runtime array in COBOL.
Start example
01  FACT-INDEX    PIC 9(4).
01  TOTAL         PIC 9(8) VALUE 1.
01  FACTORIALS.
   05  FACTORIAL OCCURS 10 TIMES PIC 9(8).
 . . .
   PERFORM VARYING FACT-INDEX FROM 1 BY 1
      UNTIL FACT-INDEX > 10
         COMPUTE TOTAL = FACT-INDEX * TOTAL MOVE TOTAL
         TO FACTORIAL (FACT-INDEX)
      END-PERFORM
End example

This code declares the table subscript FACT-INDEX, a working data item TOTAL, and FACTORIALS that holds a table of 10 elements, each of category numeric with eight digits. The intent is to store into each element the factorial of its index number. For example, entry 3 will hold 6 (3*2*1). To that end, the PROCEDURE DIVISION loops for the length of the table, computing the factorial value for this element and then moving that value into this table element. As you can see, in COBOL, table indexing starts at one, and table elements are referenced using arrayName (index) notation. Spaces before the first parentheses are optional.

Now, look at the Java equivalent of this example in Listing 6.2. This code accomplishes the same result as the COBOL code. The difference is in the indexing of the array. COBOL uses parentheses, while Java uses square brackets. As with COBOL, spaces before the first bracket are optional. Using array elements in your source code is no different than using any other regular data item, except that array elements have to be indexed.

Listing 6.2: Java code to compute, and store in an array, the factorial for numbers 0 through 9.
Start example
int fact_index;
int total = 1;
int factorials[] = new int[10];

for (fact_index=0; fact_index < 10; fact_index++)
{
   if (fact_index > 0)
      total = fact_index * total;
   factorials[fact_index] = total;
}
End example

In Java, the index of the first element of an array is zero, and the last location is the length of the array, minus one. Officially, COBOL arrays are one-based and Java arrays are zero-based. To loop through all the members of the array, the COBOL program uses the loop-index values 1 to 10, whereas Java uses 0 to 9.

Again, in both COBOL and Java, the length (number of elements) of the array is fixed, once set. For COBOL tables, the length is set at compile time, while for Java arrays, it can be set by an expression evaluated at runtime (but once set, it can't be changed). If, for any reason, the index value being used is less than one or greater than the length of the array, COBOL generates a runtime error, "index out of bound." Also, if the SSRANGE (OS/390) or RANGE (OS/400) compiler option is selected, it will terminate. Similarly, if the index value in a Java array is less than zero or greater than or equal to the length of the array, Java generates an "IndexOutOfBoundsException" error and terminates the program.

Since arrays in Java are objects, Java supplies arrays with a length member variable to retrieve the length of the array (that is, the number of elements). Knowing this, the previous example could have (and should have) been coded as in Listing 6.3.

Listing 6.3: The revised Java factorial code, using the built-in length variable for the array size.
Start example
for (fact_index=0; fact_index < factorials.length; fact_index++)
{
   if (fact_index > 0)
      total = fact_index * total;
   factorials[fact_index] = total;
}
End example

Notice that the name of the array is specified first, followed by a dot, and then the instance variable length is specified to retrieve the length of the array. This is a publicly accessible variable, not a method (surprisingly), so parentheses are not needed.

Recall that COBOL allows only the subscript or index data item between the parentheses when accessing an array entry, or the subscript plus an offset (called relative subscripting). Java, on the other hand, allows any expression that evaluates to an integer. So, in Java, you could code the following, if you chose:

myArray[myVar * 3 / 2 + 1]

Initializing Multidimensional Arrays

Now that you have seen the different types of arrays and the different ways to declare and initialize them, let's look at a few examples of array manipulations. Each value of an array is called an array element. To access an array element, you specify the name of the array with the index of that element placed between parentheses for COBOL, and square brackets for Java. In the case of multiple dimensions, COBOL separates the subscript or index for each dimension by an optional comma and a space, while Java specifies as many sets of square brackets as there are dimensions in the array. For example, if array employee is two-dimensional, then to access the first element in the first dimension, you specify employee (1, 1) in COBOL and employee[0][0] in Java.

In Java, it is important to think of multidimensional arrays as arrays of arrays. For example, for two dimensions, each element of the first dimension is actually another array. When using compile-time initialization, each element's initial value for the first dimension must be a valid initialization value for another array. You will then have nested sets of values in curly braces. Furthermore, when using the length variable, you use it per dimension. Listing 6.4, then, is an example of initializing a two-dimensional array in Java using compile-time initialization syntax, and then looping through the array to print out the values.

Listing 6.4: Initializing a two-dimensional compile-time array.
Start example
class TestMultiArray
{
   public static void main(String args[])
   {
      int ctArray[][] = {{ 1 , 2 , 3 },
                         { 4 , 5 , 6 },
                         { 7 , 8 , 9 }};
      for (int xIdx=0; xIdx < ctArray.length; xIdx++)
      {
         for (int yIdx=0; yIdx < ctArray[xIdx].length; yIdx++)
         {
            System.out.print(ctArray[xIdx][yIdx] + " ");
         } // end inner for loop
         System.out.println();
      } // end outer for loop
   } // end main method
} // end TestMultiArray class
End example

Listing 6.4 illustrates a two-dimensional array, 3-by-3 in length, using a compile-time array and assigning it the values one to nine. Note that each row of the array's initial values is surrounded by curly braces. Because there are two dimensions, you need two levels of braces, and each brace set is comma-separated, just as each element inside is comma-separated.

In the body of the code, two different loops go through the three rows and three columns in the matrix. Notice, for the length of the first array dimension, ctArray.length is used as usual, but for the second dimension, ctArray[xIdx].length is used. That is, [xIdx] must be added after ctArray to determine the length of the "sub-array" that is the column for xIdx row. Again, a two-dimensional array is really an array of arrays.

The example simply loops nine times, printing all of the elements of the array. Actually, since all rows have the same-size columns (three), you can just get away with ctArray[0].length. Finally, the statement following the inner loop is a print of a new line in order to force the output to a separate line. The following output is produced by running the example:

>java TestMultiArray
 1 2 3
 4 5 6
 7 8 9

Listing 6.5 is the same example, but as a runtime array, which gives the same output.

Listing 6.5: Initializing a runtime array in Java.
Start example
class TestMultiArrayRT
{
   public static void main(String args[])
   {
      int rtArray[][] = new int[3][3];
      int value = 1;
      for (int xIdx=0; xIdx < rtArray.length; xIdx++)
      {
         for (int yIdx=0; yIdx < rtArray[xIdx].length; yIdx++)
         {
            rtArray[xIdx][yIdx] = value++; // assign and increment value
            System.out.print(rtArray[xIdx][yIdx] + " ");
         } // end inner for loop
         System.out.println();
      } // end outer for loop
   } // end main method
} // end TestMultiArrayRT class
End example

Getting the hang of it? Listing 6.5 creates the same loop, but uses it to initialize and print out the value of each element. Note the use of value++, which, as you recall, assigns the current contents of variable value to the left side, then increments those contents by one.

Do you think you could handle expanding this example to three dimensions? Or the previous example using compile-time arrays? Any guess as to what the initialization statement might look like? Let's have a look, in Listing 6.6.

Listing 6.6: Initializing a three-dimensional array in Java.
Start example
class TestThreeArray
{
   public static void main(String args[])
   {
      int ctArray[][][]
         = { { { 1 , 2 , 3 }, { 4 , 5 , 6 }, { 7 , 8 , 9 } },
             { { 10, 11, 12}, { 13, 14, 15}, { 16, 17, 18} },
             { { 19, 20, 21}, { 22, 23, 24}, { 25, 26, 27} } };
      for (int xIdx=0; xIdx < ctArray.length; xIdx++)
      {
         for (int yIdx=0; yIdx < ctArray[xIdx].length; yIdx++)
         {
            for (int zIdx=0; zIdx < ctArray[xIdx][yIdx].length; zIdx++)
            {
               System.out.print(ctArray[xIdx][yIdx][zIdx] + " ");
            }  // end inner for loop
            System.out.print("  ");
         } // end middle for loop
         System.out.println();
      } // end outer for loop
   } // end main method
} // end TestThreeArray class
End example

The output of this is the following (although it's difficult to show three dimensions in two-dimensional space):

 1  2  3    4  5  6    7  8  9
10 11 12   13 14 15   16 17 18
19 20 21   22 23 24   25 26 27

Figure 6.1 shows what this looks like in memory, with each rectangular box representing one of the 13 arrays involved in a 3-by-3-by-3 array. (There is the array itself, then each of the three elements in the first dimension are arrays, and inside those, each of the three elements in the second dimension are again arrays.)

Click To expand
Figure 6.1: A three-dimensional array in memory. Each rectangle is an array.

Finally, Listing 6.7 is the same example coded in COBOL, although the array values are initialized at runtime in this case.

Listing 6.7: Initializing a three-dimensional array in COBOL.
Start example
IDENTIFICATION DIVISION.
PROGRAM-ID.     THREEDIM.
AUTHOR.         Phil Coulthard.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  XIDX    PIC 99 VALUE ZERO.
01  YIDX    PIC 99 VALUE ZERO.
01  ZIDX    PIC 99 VALUE ZERO.
01  COUNTER PIC 99 VALUE ZERO.
01  CTARRAY.
   05 DIM1 OCCURS 3 TIMES.
      10 DIM2 OCCURS 3 TIMES.
         15 DIM3 OCCURS 3 TIMES PIC 99.
PROCEDURE DIVISION.
000-MAIN.
   PERFORM VARYING XIDX FROM 1 BY 1 UNTIL XIDX > 3
      AFTER YIDX FROM 1 BY 1 UNTIL YIDX > 3
         AFTER ZIDX FROM 1 BY 1 UNTIL ZIDX > 3
            COMPUTE COUNTER = COUNTER + 1
            MOVE COUNTER TO DIM3 (XIDX, YIDX, ZIDX)
            DISPLAY DIM3 (XIDX, YIDX, ZIDX)
   END-PERFORM
   STOP RUN.
END PROGRAM THREEDIM.
End example

Arrays Are Objects

Java uses references (or, if you like, pointers) to point to objects. Array references are no different than any other Java object references. For example, you can point to one array object with an array reference, and later point to another array object using the same reference. Listing 6.8 illustrates this.

Listing 6.8: An example to illustrate that array variables in Java are, in fact, references to array objects.
Start example
class TestArrayObjects
{
   public static void main(String args[])
   {
      int arrayOne[][] = { { 1 , 2 }, { 3 , 4 } };
      int arrayTwo[][] = { { 5 , 6 }, { 7 , 8 } };
      int tempArray[][];
      tempArray = arrayOne;
      for (int xIdx = 0; xIdx < tempArray.length; xIdx++)
      {
         for (int yIdx = 0; yIdx < tempArray[xIdx].length; yIdx++)
            System.out.print(tempArray[xIdx][yIdx] + " ");
         System.out.println();
      }
      tempArray = arrayTwo;
      for (int xIdx = 0; xIdx < tempArray.length; xIdx++)
      {
         for (int yIdx = 0; yIdx < tempArray[xIdx].length; yIdx++)
            System.out.print(tempArray[xIdx][yIdx] + " ");
         System.out.println();
      }
   } // end main method
} // end TestArrayObjects class
End example

Listing 6.8 declares and initializes two arrays, arrayOne and arrayTwo. It then declares, but does not create, a third array, tempArray, of the same dimensions as the first two. The body of the code first assigns tempArray to arrayOne and loops through, printing the contents, then reassigns tempArray to arrayTwo and loops through, again printing the contents. This results in the following output:

1 2
3 4
5 6
7 8

You might be inclined to think that each assignment of tempArray to another array results in a copy of the contents from one array to another. This is not the case, specifically because arrays are objects in Java, and array variables are, therefore, reference variables. Thus, assigning a reference variable is the same as changing the object it points to in memory. So, the tempArray assignments simply change the array to which the tempArray variable points, with no copying of array contents. This means changes made to the array elements of tempArray also affect the original array to which it points. That's because after an array assignment, a statement like

tempArray = arrayTwo;

results in one array in memory, but two variables referencing the array.

If you did want to copy the contents of an array, you would first have to instantiate a new instance of an array for the target using the new operator, then loop through both arrays, copying the contents. While this is easy enough to code, there is an easier way if you do actually want to create a duplicate copy of an array. In fact, there are two easier ways. The first is to use the clone method that arrays support, which returns a copy of the array, as follows:

int arraySrc[] = {1, 2, 3, 4};
int arrayTgt[] = (int[])arraySrc.clone();

Note how you have to cast the result to an integer array using (int[]). Yes, you can cast objects, and arrays are objects. More information on casting of objects is available in chapter 9, but for now, just remember that you must use this syntax to cast the result of clone() to an array of the target array type.

The second option is to use the Java-supplied static method in the System class called arraycopy to handle this for you. It takes as input a source array, an index position from which to start copying, a target array, an index position in the target array where the copying starts to go, and a length indicating how many elements to copy. Here is an example, using a single-dimensional array:

int arraySrc[] = {1,2,3,4};
int arrayTgt[] = new int[4];
System.arraycopy(arraySrc, 0, arrayTgt, 0, 4);

Both of these options work well for single-dimensional arrays, but for multidimensional arrays, you must be careful to call them per dimension, in the right order. For those, we recommend you simply use your own code loop instead, for peace of mind.

Unbalanced Arrays

So far, the multidimensional arrays have all been rectangular, with the same number of elements in each dimension. Because Java's multidimensional arrays are actually arrays of arrays, however, each dimension does not have to be the same length. Each element in a given dimension, if it is another array, can have differing numbers of elements. This is called an unbalanced array.

The main restriction in unbalanced arrays is for the dimensions to be specified left to right. If you are declaring, say, an array of ten elements, and each element of the array is actually another array of unknown length, you can do the following:

int myArray[][] = new int[10][];

In this case, you specify the first left dimension as 10. You leave the second unknown dimension to be declared or created later, in the body of the code. This example is perfectly legal in Java. However, you cannot do the opposite, and specify the second dimension before the first, like this:

int myArray[][] = new int[][10];  // NOT VALID!

This is what is meant by the left-to-right rule in Java. It is legal to specify the left dimension and leave the right, but not legal the other way around. The fact that multidimensional arrays in Java are arrays of arrays explains this restriction. You create the left array first, and as you proceed in the code, you can create the rest, left to right.

Consider the following example that uses unbalanced arrays:

boolean ubArray[][] = new boolean[3][];
for (int idx = 0; idx< ubArray.length;idx++)
{
   ubArray[idx] = new boolean[idx+1];
}

In this example, an array of length 3 is created for the first dimension, and an array of unknown length is created for the second dimension. As you see, the code loops three times, building up the second dimension of the array. The body of the for loop uses the new operator to create the second dimension. This is, in fact, a new array for each element. The result of this is an array containing the following values:

false
false false
false false false

As you can see, you start with the first row having only one element, the second row having two elements, and the third row having three elements. This is a result of using idx+1 as the length parameter when creating the new array for each row.

When and why might you use such a thing? Imagine you run a store that has five departments, and each department has differing numbers of salespeople. Suppose department 1 has two salespeople, department 2 has seven salespeople, department 3 has one salesperson, department 4 has five salespeople, and department 5 has the most, at 20 salespeople (maybe it sells perfume). To track the employee IDs of each salesperson by department, you could declare an array of 5 by 20, but that would waste space for most departments. Instead, you could create a two-dimensional array, the first dimension being of length 5, and the second being of a unique length per department, a number which (for example) you read out of a database.


Arrays Of Objects

In COBOL, you do not always create tables that contain single-level items. Instead, you often create tables of group items. For example, suppose you have a table of departments. Each element in the table will probably contain numerous items:

  • The number of the department.

  • The name of the department.

  • The employee ID of the manager of the department.

  • A count of the number of salespeople.

  • An embedded array of the salespeople, with their identifications.

For example, look at Listing 6.9.

Listing 6.9: A definition of a table of group items in COBOL.
Start example
01  DEPARTMENT-TABLE.
   05 DEPARTMENT OCCURS 5 TIMES.
      10 DEPT-NBR       PIC 999.
      10 DEPT-NAME      PIC X(10).
      10 DEPT-MGR-ID    PIC 999999.
      10 DEPT-NBR-SALES PIC 99.
      10 DEPT-SALES     OCCURS 20 TIMES.
         15 DEPT-SALES-ID PIC 999999.
End example

How would you do this in Java? Java doesn't have group items or structures, but it does have classes. You would create a class for an individual department, then, in another class, you would declare an array of department objects, and instantiate each department element. The constructor for the department class would probably take as a parameter an open database-connection object, and use it to read the values for the next department record from the database and populate the instance variables with the results. Listing 6.10 shows an example of the Department class. (Since database access is not covered until chapter 13, that part is omitted from the code.)

Listing 6.10: Using classes to define the same table as in Listing 6.9.
Start example
public class Department
{
   private int    deptNbr;
   private String deptName;
   private int    deptMgrId;
   private int    deptNbrSales;
   private int    deptSales[] = new int[5];
   public Department()

   {
      // read next department record from database
      // populate above instance variables from database
      // instantiate deptSales array once we know how many in dept
   }

   public int getDeptNbr()
   {
      return deptNbr;
   }
   public String getDeptName()
   {
      return deptName;
   }
   public int getDeptMgrId()
   {
      return deptMgrId;
   }
   public int[] getDeptSales()
   {
      return deptSales;
   }
} // end class Department
End example

Notice that Listing 6.10 makes the variables private, instead supplying "getter" methods to retrieve the values. That is good practice, as it allows you to change the attributes of those variables later and minimize the impact on other code using this class. The other class that creates the array of Department objects is shown in Listing 6.11. Notice that for arrays of objects, you have to instantiate the array object itself, as usual, and you also have to instantiate the objects in each element in the array, as usual for objects, whether they are in an array or not.

Listing 6.11: A Java class containing an array of objects, each of type Department.
Start example
public class Departments
{
   private Department depts[] = new Department[5]; // create array

   public Department() // constructor
   {
      // open database connection. Not shown
      for (int idx = 0; idx < depts.length[]; idx++)
      {
         depts[idx] = new Department(); // pass db connection not shown
      }
   }
} // end class Departments
End example

There are a number of other things here that you could turn into separate classes, as well. For example, you could make a class called EmployeeID that would hold the six-digit employee ID for each employee. This way, you could easily change that to seven digits later, and not affect anyone. You might also make a single class called SalesTeam that contains both the number of salespeople in this team and the actual embedded array of salespeople Employee IDs. This way, you could easily add other information and methods later, such as getTotalSales, printReport, and so on, which could be easily used whenever and wherever you had a SalesTeam object.


Helper Methods For Arrays

In COBOL, you often search an array (that is, a table) using the SEARCH statement. The language gives this nice functionality to save you writing your own redundant search algorithm every time. It would be nice if Java gave similarly capabilities, would it not? Well, if you are using Java's JDK prior to 1.2.0, the unfortunate answer is that Java does not. You have to code your own search algorithm, as in Listing 6.12.

Listing 6.12: An example of a search algorithm.
Start example
/**
* Method to search integer array for given integer
* @param array of integers to search
* @param integer to search for
* @return index in array if argument found, else -1
*/
public static int searchArray(int searchArray[],
                              int searchArgument)
{
   int matchIndex = -1;
   for (int idx=0; idx<searchArray.length && matchIndex < 0; idx++)
      if (searchArray[idx] == searchArgument)
         matchIndex = idx;
   return matchIndex;
} // end of SearchArray method

/**
* command line control for testing
*/
public static void main(String args[])
{
   System.out.println("Welcome to class SearchArray");
   int testArray[] = {1,2,3,4,5,6,7,8,9};
   int match = searchArray(testArray, 7);
   System.out.println(match);
   match = searchArray(testArray, 0);
   System.out.println(match);
} // end main
End example

The method is made static because it doesn't depend on any instance variables, but instead takes what it needs as parameters. This example will only work, however, with an integer array, and therefore an integer search argument, since the two parameters are declared to the method to be of type int, and so only int parameters can be passed. To support arrays of other types, like the other seven primitive data types, you would create overloaded versions of this method. Recall that overloaded methods are methods with the same name, but with a different number or type of parameters. So, you could copy this searchArray method and produce versions that accept a float array plus float search argument, a double array plus double search argument, and so on.

This is something you might want to do if you need to search arrays often. However, if you use JDK 1.2.0 or higher, a series of such helper methods is already supplied in the Arrays class of the java.util package. Table 6.2 lists the methods (all static) in this class (not a complete list).

Table 6.2: Methods in the java.util.Arrays Class.
Method | Description
binarySearch(type array[], type key) | Overloaded methods for searching for the given key or search argument. There are methods for seven of the primitive types, but not Boolean, and for arrays of objects. The array must already be sorted for this efficient binary search to work, just as with COBOL's SEARCH ALL statement. It returns the zero-based index position of the match if found, otherwise it returns ( (insertion point) -1), where insertion point is where the item should be inserted.
equals(type array1[], type array2[]) | Compares two arrays returning true if they have the same number of elements and the element values are the same. Overloaded methods for all eight of the primitive types, plus arrays of objects.
fill(type array[], type fillValue) fill(type array[], int fromIndex, int toIndex, type fillValue) | Fills all elements of the given array with the given value. Overloaded methods for all eight of the primitive types, plus arrays of objects. Also, there is a version for each that allows you to fill just a given range of elements.
sort(type array[]) sort(type array[], int fromIndex, int toIndex) | Sorts the given array. Overloaded methods for all eight of the primitive types, plus for arrays of objects. Also, there is a version for each that allows you to sort just a given range of elements

The type shown for the method signatures in the left column of Table 6.2 is just a placeholder, which you can replace with each of the primitive types, as in

sort(int array[])

Note that all the array methods work on objects and primitive types. What possible type is used that will work for all classes, versus just the one that is specified on that overloaded version of the methods? The answer is Object, a class in the java.lang package. It turns out that, if you specify a type of Object for a method parameter, you can actually pass in objects of any class. (Chapter 9 explains why this is true, but for now, just remember this fact.)

Another thing to think about is, given an array of objects, how can Java sort the array? How does it know how to compare any two object elements, in order to sort them? Java does not know what it means for one object to be greater than another. The answer is that it leaves this decision up to the objects themselves!

When comparing two objects, Java tries to call a particular method on one of the objects, a method called compareTo, passing in the second object as a parameter. That method returns a negative number, zero, or a positive number if the second object is less than, equal, or greater than the target object, respectively. So, to sort an array of objects of your class type, your class must have this method in it. Most Java-supplied classes, like String, already supply this method. For you to supply it in your classes, you have to implement an interface called Comparable. (To learn more about interfaces, see chapter 9.)

You might have noticed there are no search methods, only binarySearch methods. This is a bit of surprise for us, too, and it means if you have arrays that are not sorted, you have to write your own search methods like the one shown in Listing 6.12. However, the good news is that it is easy to sort an array using the sort methods.

You might also have noticed that these methods all work only on single-dimensional arrays. That is true, but multidimensional arrays are actually arrays of arrays, so with only a little bit of extra work, you could call these methods on all the dimensions of a multidimensional array. For example, the following will fill all entries of a 10-by-10 integer array with –1:

import java.util.*; // for Arrays class
    . . .
int myArray[][] = new int[10][10];
for (int rows=0; rows < myArray.length; rows++)
   Arrays.fill(myArray[rows],-1);

These methods are all pretty straightforward to use, so we will just show you an example in Listing 6.13 of the sort method, followed by the binarySearch method.

Listing 6.13: A sort method followed by a binarySearch method.
Start example
public class SortedArray
{
   public static void main(String args[])
   {
      int sortedArray[] = {48,25,12,34,10,52,70,16,35};
      Arrays.sort(sortedArray);
      System.out.println();
      for (int idx=0; idx < sortedArray.length; idx++)
         System.out.print(sortedArray[idx] + " ");
      System.out.println();
      int newpos = Arrays.binarySearch(sortedArray,15);
      if (newpos < 0)
         {
            newpos = -newpos - 1;
            System.out.println(newpos);
         }
   }
}
End example

This simple example prints out an empty line, then declares a compile-time array of unsorted integers, and subsequently sorts the array and prints the sorted results to verify that it worked. Note that System.out.print versus System.out.println prints the given data without advancing the console by a new line. Finally, binarySearch is used on the array and the number 15 to see what we get back. The result of running this code snippet is the following:

10 12 16 25 34 35 48 52 70
2

Got your fill of arrays? Sort of? Let's search for something new.


Vectors

In the previous section, you learned about single- and multiple-dimensional arrays. One important restriction in arrays that applies to both COBOL and Java is that their length, once set, can never change, although in Java, you can dynamically declare an array. Yes, this is more powerful than COBOL, as it allows you to defer creation until you know the length. However, once you create an array, you cannot change its length. In other words, existing arrays have fixed lengths, and they cannot grow or shrink.

As you tackle more complex applications, you might not know at any time, whether at compile time or runtime, how many elements you want to store. To help with this, Java supplies a utility class, called Vector, in its java.util package. Java uses vectors as an alternative when you need single-dimensional arrays that can grow or shrink dynamically. COBOL supports this, sort of, with its OCCURS DEPENDING ON clause, but you still have to supply the minimum and maximum table size.

Note

A vector is an adjustable array that can increase or decrease its length in response to the number of elements it needs to store. Vectors use the term size versus length to define the number of elements.

Variables of the Vector class are (you guessed it) objects in Java, just as arrays are. However, unlike arrays, vectors have no hard-wired language support, such as special syntax for instantiating and indexing. The Vector class is just a regular class, just as you could write on your own. It contains many supplied methods to help you with adding, removing, and searching elements. Listing 6.14 illustrates a simple vector with three elements.

Listing 6.14: A simple vector with three elements.
Start example
import java.util.*; // import all classes in java.util package
class TestVector
{
   public static void main(String args[])
   {
      Vector myFirstVector = new Vector();
      myFirstVector.addElement(new String("Ford"));
      myFirstVector.addElement(new String("GM"));
      myFirstVector.addElement(new String("Chrysler"));
      for (int idx = 0 ; idx < myFirstVector.size() ; idx++)
      {
         System.out.println(myFirstVector.elementAt(idx));
      }
   }
}
End example

In this example, you first define and create the Vector variable myFirstVector. You then add elements to it. Notice the use of the String class, as elements are added. (Strings are the topic of the next chapter.)

For now, all you need to know is that the new keyword with the String class creates a String object, and the addElement method adds that string to the vector. For example, the first case sends the string "Ford" as the parameter to the String class constructor to build a new String object with that value. The reference of that object is used as the input parameter to the addElement method of the Vector class. The same thing happens two more times, adding three strings to the vector, after which the for loop prints the content of the vector. Looping stops when the index variable reaches the size of the vector, determined by using the size method.

Note

Vectors can only contain objects, not primitive type values. To use primitive values, you must create an instance of their wrapper classes, such as java.lang.Integer.

This example uses two methods that the Vector class supports, namely addElement and size. There are many more methods that the Vector class supports; the most commonly used ones are listed in Table 6.3. (For a complete and up-to-date list of these methods, look up the online documentation in the JDK.)

Table 6.3: Methods in the java.util.Vector Class.
Method | Description
addElement(Object) | Adds the specified object to the end of this vector, increasing its size by one.
capacity() | Returns the current capacity of this vector. Capacity is the potential size of a vector, versus its actual size or element count.
clone() | Returns a copy of this vector. All elements are copied.
contains(Object) | Tests if the specified object is an element in this vector.
copyInto(Object[]) | Copies the elements of this vector into the specified array.
elementAt(int) | Returns the element at the specified index (zero-based).
elements() | Returns an enumeration of the elements of this vector. (See chapter 9 for information on enumerators.)
ensureCapacity(int) | Increases the capacity of this vector to the given number.
firstElement() | Returns a reference to the first element of this vector.
indexOf(Object) | Searches for the first occurrence of the given parameter, testing for equality using the equals method on each element.
indexOf(Object, int) | Searches for the first occurrence of the first parameter, beginning the search at the zero-based index specified in the second parameter, and testing for equality using equals.
insertElementAt(Object, int) | Inserts the specified object at the specified index.
isEmpty() | Tests if this vector has no elements.
lastElement() | Returns the last element of the vector.
lastIndexOf(Object) | Returns the index of the last occurrence of the specified object in this vector. Useful for repeated elements.
lastIndexOf(Object, int) | Searches backwards for the specified object, starting from the specified index, and returns an index to it.
removeAllElements() | Removes all elements from this vector, and sets its size to zero.
removeElement(Object) | Removes the first occurrence of the argument from this vector.
removeElementAt(int) | Deletes the element at the specified index.
setElementAt(Object, int) | Sets the element at the specified index to the specified object.
setSize(int) | Sets the size of this vector.
size() | Returns the number of elements in this vector.
toString() | Returns a string representation of this vector.
trimToSize() | Trims the capacity of this vector to be the vector's current size.

So far, you have seen a straightforward, simple example of a vector. You will find yourself using vectors a lot in Java, whenever you need a list whose number of elements you cannot accurately predict at compile time or even compute at runtime (since you can defer array instantation until just before you need the array). Let's look at a slightly more complex example that uses a few vector methods.

Suppose you are writing a program to register attendees at an event of some kind, maybe a sporting event. You want to track all who enter and all who leave, and because you can't predict the exact number of attendees, you decide to use a vector to hold the list of names. The program should accept commands from the console and act appropriately on the commands entered. The commands accepted will be R to register an attendee, U to "unregister" an attendee, D to display a list of attendees, and E to exit the program. When registering and unregistering, the program should prompt for an attendee name, which is what is stored in the vector. Listing 6.15 is an outline of the logic needed.

Listing 6.15: An example of vector logic.
Start example
public void run()
{
   String command = "Not set";
   while (!command.equalsIgnoreCase("E"))
   {
      command = readFromConsole("Enter command
         (R=Register, U=Unregister, D=Display, E=Exit)");
      if (command.equalsIgnoreCase("R"))
         // prompt for name and record in a vector
      else if (command.equalsIgnoreCase("U"))
         // prompt for name, verify it is registered, and
            remove from vector
      else if (command.equalsIgnoreCase("D"))
         // loop through vector displaying all names
   } // end while loop
} // end run method
End example

The vector will be declared as an instance variable, as follows:

private Vector attendees = new Vector();

First, you need code to prompt the user on the console and then read in what is typed. This is shown as a call to method readFromConsole, which takes as a parameter the prompt string to write out, and returns the string typed in by the user. The readFromConsole method is shown in Listing 6.16. Once the text has been entered, it is compared to R, U, and D; you will need to write code to act appropriately on these. Note that to compare one string to another, independent of case, you use the method equalsIgnoreCase in the String class. The loop ends when the user types E, forcing exit.

Listing 6.16: An example of the readFromConsole method.
Start example
/**
*Write out prompt then read a line from console
*@param the string to write out first for the prompt
*@return the string read in from the console
*/
public static String readFromConsole(String prompt)
{
   String userInput = "";
   System.out.println(prompt);
   InputStream incmd = System.in;
   DataInputStream inlinedata = new DataInputStream(incmd);
   BufferedReader inFileStream = new BufferedReader(
                  new InputStreamReader(inlinedata) );
   try
   {
      userInput = inFileStream.readLine();
   }
   catch (IOException exc)
   {
      System.out.println("File error: " + exc);
   }
   return userInput;
} // end readFromConsole method
End example

You can access the standard output stream through the System class. This was shown in many of the preceding examples that used the print and println methods. Similarly, you can access the standard input stream through the System class. The standard input stream lets you accept user input through the console window.

In this example, you need to define the input stream using the DataInputStream class to read in the data as shown in Listing 6.16. BufferedReader is used to take advantage of the readLine method, which reads a whole line at once from the console, versus one character at a time. Since the readLine method can throw exceptions, a try/catch block is needed to monitor for them. (These are explained in chapter 10. For now, just accept that this method works.) This is all you need to get the input from the console. Note that the classes used in this method are all defined in the java.io package, so you must import java.io.* at the top of this class.

Referring back to the pseudocode in Listing 6.15, let's look at what you need to do for each case.

To register a user, you prompt for his or her name, and add that name to the attendees vector using the Vector method addElement, as follows:

{
   String name = readFromConsole("Enter name:");
   attendees.addElement(name);
}

This is pretty straightforward. Next, for unregistering, you need to prompt for the name and then query if it is in the attendees vector using the Vector method indexOf. If it is found, use the index position returned by that method call as input into the Vector method removeElementAt, as follows:

{
   String name = readFromConsole("Enter name:");
   int    pos  = attendees.indexOf(name);
   if (pos < 0)
      System.out.println("Sorry, that name is not registered");
   else
      attendees.removeElementAt(pos);
}

Finally, to process the display request, simply loop through the attendees vector and display the contents of each entry:

{
   for (int idx = 0; idx < attendees.size(); idx++)
      System.out.println("Name:" + attendees.elementAt(idx));
}

The Vector class can locate an element at a specified zero-based position using the elementAt method, as shown. This is similar to the removeElementAt method used previously. You supply as a parameter to the method the index of the element you want printed, which is the current loop index used to loop through all of the elements in the vector. In addition, this code takes advantage of the size() method in the looping expression to return the current number of elements in the vector you are dealing with.

This is all you need to accomplish the goals of the registration program. Listing 6.17 shows the entire example, including a main method that instantiates the class and calls the run method on it.

Listing 6.17: A Java class to register and "unregister" names. A vector is used to store the names.
Start example
import java.util.*; // for Vector class
import java.io.*; // for console reading
classes public class Registration
{
      private Vector attendees = new Vector();

   public void run()
   {
      String command = "Not set";
      while (!command.equalsIgnoreCase("E"))
      {
         command = readFromConsole("Enter command (R=Register,
                  U=Unregister, D=Display, E=Exit)");
         if (command.equalsIgnoreCase("R"))
            {
               String name = readFromConsole("Enter name:");
               attendees.addElement(name);
            }
         else if (command.equalsIgnoreCase("U"))
            {
               String name = readFromConsole("Enter name:");
               int    pos  = attendees.indexOf(name);
               if (pos < 0)
                  System.out.println("Sorry, that name is not registered");
               else
                  attendees.removeElementAt(pos);
            }
         else if (command.equalsIgnoreCase("D"))
            {
               for (int idx = 0; idx < attendees.size(); idx++)
                  System.out.println("Name:" + attendees.elementAt(idx));
            }
      } // end while loop
   } // end run

   public static String readFromConsole(String prompt)
   {
      String userInput = "";
      System.out.println(prompt);
   InputStream incmd = System.in;

   DataInputStream inlinedata = new DataInputStream(incmd);
   BufferedReader inFileStream = new BufferedReader(
                  new InputStreamReader(inlinedata) );
   try
   {
      userInput = inFileStream.readLine();
   }
   catch (IOException exc)
   {
      System.out.println("File error: " + exc);
   }
      return userInput;
   } // end readFromConsole method

   public static void main(String args[])
   {
      System.out.println("Welcome to class Registration");
      Registration register = new Registration();
      register.run();
   } // end main
} // end of class Registration
End example

Running this class and entering some sample commands gives the following (with the user input shown in bold):

Welcome to class Registration
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
R
Enter name:
George Farr
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
R
Enter name:
Phil Coulthard
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
R
Enter name:
Lou Gerstner
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
D
Name:George Farr Name:Phil Coulthard Name:Lou Gerstner
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
U
Enter name:
Phil Coulthard
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
D
Name:George Farr Name:Lou Gerstner
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
U
Enter name:
Bad Name
Sorry, that name is not registered
Enter command (R=Register, U=Unregister, D=Display, E=Exit)
E

This example can be modified to add more information for each attendee, such as a telephone number, so as to avoid name collisions. Also, some error-checking is always welcome.


Hashtables

You have seen how to create single- and multiple-dimensional arrays, and even arrays of objects. You also saw how another class, Vector, is better suited than arrays when you want to dynamically grow and shrink the list on demand. There are other times, as well, when an array is not the best choice to store a list of items. For example, what about lookup tables? Java has a special class for situations when data needs to be efficiently found using a key value: the Hashtable class in the java.util package.

Hashtables are used to map keys to values. Any object can be used as a key or as a value. Like the Vector class, the Hashtable class does not permit primitive types, so to store integers, for example, you must use the Integer wrapper class, as in the following:

new Integer(5)

This stores the number 5, and integerObject.intValue()returns the primitive value out of the object later.

The reason to use a hashtable over an array or a vector is strictly performance. In dealing with a large number of values that are keyed by a unique value, hashtables can offer good performance benefits when searching for that key. For example, in the registration example, you might choose a hashtable if you decide to ask not only for the attendee names, which are not unique, but also for their telephone numbers, which are unique.

You could use a pair of arrays or a pair of vectors, or preferably create an Attendee class that holds both the telephone number and the name of all attendees. But how would you search for a particular attendee, given a telephone number? You would have to search the array elements (Attendee objects) sequentially until you found one that had a matching telephone number. As the list gets large, this could be slow. You could sort the array and then use the binarySearch helper method in Arrays to search efficiently, but that would require a re-sort of the array after every entry, which would also be slow.

A good solution to this problem is to use a Hashtable object instead of an array or vector. With Hashtable, you add elements using the put method and pass in two parameters, the key value object and the value for that key, as follows:

Hashtable attendees = new Hashtable();
attendees.put("5551112222","Phil Coulthard");
attendees.put("5551113333","George Farr");

This examples puts two entries in the hashtable; one has the key 5551112222 and the value Phil Coulthard, and the second has the key 5551113333 and the value George Farr. Although String objects are used here for both the key and values, any object is valid. Further, you do not have to use the same object type for all keys or all values. Internally, the key value is "hashed" into a number within a particular range, which is then used as the index into an internal table. The given value is then stored at that table position, as shown in Figure 6.2.

Click To expand
Figure 6.2: Java's Hashtable hashes the key into a unique index in an internal table, for storing the value.

To subsequently retrieve a value, you use the get method and supply the value's key, as follows:

String name = (String)attendees.get("5551112222");
if (name != null)
   System.out.println(name);

Notice you have to cast the output of the get method to the actual class type of the value stored for that key. Again, casting of objects is covered in chapter 9, but it is required here because Hashtable only knows generically about Object objects, and so to use retrieved values, you have to tell Java exactly what the type of that value is. If no value is found with that key, get will return null, which you should test for.

To retrieve a list of all the values in a hashtable, use the elements method. This returns an object of class type Enumeration (found in java.util, as well). Enumeration objects are a list, with two simple methods for traversing it: hasMoreElements()returns true while there are more elements in the list, and next()returns the next element in the list, for example:

Enumeration names = attendees.elements();
while (names.hasMoreElements())
   System.out.println((String)names.next());

Note that you have to cast the result of next like you did with get, since Enumeration is also a generic helper class that works generically on objects of any type. There is also a keys method in Hashtable for similarly returning an enumeration of the keys. You will find that many Java-supplied classes containing lists have methods that return an Enumeration object.

There are lots of methods in the Hashtable class beyond put, get, and elements. Table 6.4, while not a complete list, shows some of the more interesting methods.

Table 6.4: Methods in the java.util.Hashtable Class.
Method | Description
clear() | Clears all keys from the table.
contains(Object value) or containsValue(Object value) | Returns true if the given value is in the table. Note that it's possible to have multiple keys with the same value.
containsKey(Object key) | Returns true if a given key is in the table. Note that keys are always unique.
elements() or keys() | Returns the enumeration of the values or the keys.
isEmpty() | Returns true if the table is empty.
put(Object key, Object value) and get(Object key) | Enters a value keyed by a key, and gets the value for a given key.
remove(Object key) | Removes an entry from the table.
size() | Returns the number of elements currently in the table.

Finally, Listing 6.18 is the original Registration class created for vectors, redone to use both telephone numbers and names, and to use a hashtable to store the names keyed by telephone numbers. Changes from the vector version are shown in bold, and the class is renamed to RegistrationViaPhoneNumber.

Listing 6.18: A revised Java class to register names. A hashtable is used to store the names, keyed by telephone number.
Start example
import java.util.*;
import java.io.*;
public class RegistrationViaPhoneNumber
{
   private Hashtable attendees = new Hashtable();

   public void run()
   {
      String command = "Not set";
      while (!command.equalsIgnoreCase("E"))
      {
         command = readFromConsole("Enter command (R=Register,
               U=Unregister, D=Display, E=Exit)");
         if (command.equalsIgnoreCase("R"))
            {
               String number = readFromConsole("Enter phone number:");
               String name   = readFromConsole("Enter name:");
               attendees.put(number,name);
            }
         else if (command.equalsIgnoreCase("U"))
         {
         String number = readFromConsole("Enter phone number:");
         String name   = (String)attendees.get(number);
         if (name == null)
            System.out.println("Sorry, that number is not registered");
         else
            {
               attendees.remove(number);
               System.out.println("Name " + name + " removed");
            }
         }
      else if (command.equalsIgnoreCase("D"))
         {
            Enumeration numbers = attendees.keys();
            while (numbers.hasMoreElements())
            {
               String number = (String)numbers.nextElement();
               String name   = (String)attendees.get(number);
               System.out.println("Number: " + number + ", name: " + name);
            }
         }
      } // end while loop
   } // end run

   public static String readFromConsole(String prompt)
   {
      // *** SAME AS IT WAS ***
   } // end readFromConsole method

   public static void main(String args[])
   {
      System.out.println("Welcome to class RegistrationViaPhoneNumber");
      RegistrationViaPhoneNumber register = new
            RegistrationViaPhoneNumber();
      register.run();
   } // end main
} // end of class RegistrationViaPhoneNumber
End example


Parameter Passing

How do you pass arrays as a parameter? Or can you do that in COBOL and Java? The answer is yes you can, for both languages. The only difference between the two languages is whether they allow passing arrays by value, reference, or both. Pass by value means that the calling program passes a copy of the array and its values to a called procedure. If the called procedure alters the values of the array, the original values in the calling program is not affected. COBOL also has a version of this called pass by content, where instead of passing a whole copy of the parameter, an address of a copy is passed, which is more efficient. Contrast both of these to pass by reference, where the address of the original array is passed to the called procedure, and any alterations to the array values are reflected in the original program.

Consider Listing 6.19, a COBOL program in which both calls by value and calls by reference are allowed. Two nested programs change the values of the given array, and the main, outermost program calls the nested programs, passing an array first by value, then by content, and finally by reference.

Listing 6.19: A COBOL program highlighting the differences between passing parameters by value, content, and reference.
Start example
IDENTIFICATION DIVISION.
PROGRAM-ID.     PARMS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 SUB PIC 99.
01 WEEK-TABLE VALUE "MONTUEWEDTHUFRISATSUN".
   05 DAYS OCCURS 7 TIMES PIC XXX.
PROCEDURE DIVISION.
000-MAIN.
   CALL "BYVALUE" USING BY VALUE WEEK-TABLE.
   PERFORM DISPLAY-ARRAY-VALUES.
   CALL "BYREFERENCE" USING BY CONTENT WEEK-TABLE.
   PERFORM DISPLAY-ARRAY-VALUES.
   CALL "BYREFERENCE" USING BY REFERENCE WEEK-TABLE.
   PERFORM DISPLAY-ARRAY-VALUES.
   STOP RUN.
DISPLAY-ARRAY-VALUES.
   PERFORM VARYING SUB FROM 1 BY 1 UNTIL SUB > 7
      DISPLAY DAYS (SUB)
   END-PERFORM.

******************************
* NESTED PROGRAM BYVALUE     *
******************************
IDENTIFICATION DIVISION.
PROGRAM-ID.     BYVALUE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 SUB PIC 99.
LINKAGE SECTION.
01 ARRAY-PARM.
   05 ARRAY-ELEMENT OCCURS 7 TIMES PIC XXX.
PROCEDURE DIVISION USING BY VALUE ARRAY-PARM.
000-MAIN.
   PERFORM VARYING SUB FROM 1 BY 1 UNTIL SUB > 7
      MOVE "XXX" TO ARRAY-ELEMENT (SUB)
   END-PERFORM
   EXIT PROGRAM.
END PROGRAM BYVALUE.
******************************
* NESTED PROGRAM BYREFERENCE *
******************************
IDENTIFICATION DIVISION.
PROGRAM-ID.     BYREFERENCE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 SUB PIC 99.
LINKAGE SECTION.
01 ARRAY-PARM.
   05 ARRAY-ELEMENT OCCURS 7 TIMES PIC XXX.
PROCEDURE DIVISION USING BY REFERENCE ARRAY-PARM.
000-MAIN.
   PERFORM VARYING SUB FROM 1 BY 1 UNTIL SUB > 7
      MOVE "XXX" TO ARRAY-ELEMENT (SUB)
   END-PERFORM
   EXIT PROGRAM.
END PROGRAM BYREFERENCE.
END PROGRAM PARMS.
End example

The nested programs BYVALUE and BYREFERENCE are identical, in that both take as a parameter an array of seven, three-character strings, and move XXX into each array element. The difference is only in the PROCEDURE DIVISION statement, which identifies the array parameter as USING BY VALUE in the first program and BY REFERENCE in the second. The outermost program declares an array containing the three-character short forms for each day of the week. The BYVALUE subprogram is called with that array, passing it BY VALUE, and then printing out the results of the array to see if they were altered by the nested program call. Then, the BYREFERENCE nested program is called twice, once specifying BY CONTENT and once specifying BY REFERENCE. After each call, the contents of the array are displayed to see if the program call had any effect on the actual array.

The result of compiling and running this program is that the first two printings of the array show its contents unchanged, while the third printing shows it contains XXX for each element. This confirms that only pass-by-reference calls allow the subprogram to affect the values of the arrays.

In Java, this is not the case. As mentioned in chapter 2, Java does not support pointers. Java methods normally cannot change a parameter's value. In other words, Java normally passes parameters to methods using pass-by-value, similar to indicating the USING BY VALUE phrase for COBOL program calls. However, Java does pass non-primitive types such as objects, arrays, and vectors by reference. This lets the method change the content of the parameter. Actually, though, even these cases are pass-by-value; it is just that the reference itself is passed by value.

That is, object reference variables are, after all, memory addresses. When you pass an object, including an array, which, after all, is an object, you are passing a memory address. That address itself is passed by value, so if you reset the object to which the variable points, perhaps by equating it to another object or a new object created with the new operator, that address change will not be reflected in the calling code. Any changes made to the instance variables in the object, by invoking methods on the object or changing elements in an array, are reflected to the caller. Listing 6.20 is an example to illustrate all of this.

Listing 6.20: A Java class highlighting that elements of an array parameter can be changed in the method, but the array reference itself cannot be changed.
Start example
import java.util.*;
public class TestArrayParameters
{
   public static void main(String args[])
   {
      String days[] = {"mon","tue","wed","thu","fri","sat","sun"};
      System.out.println();
      printElements("before", days);
      changeArrayElements(days);
      printElements("after element changes ", days);
      changeArrayReference(days);
      printElements("after reference change", days);
   }
   public static void changeArrayElements(String givenArray[])
   {
      Arrays.fill(givenArray,"XXX");
      return;
   }
   public static void changeArrayReference(String givenArray[])
   {
      givenArray = new String[7];
      Arrays.fill(givenArray,"YYY");
      return;
   }
   public static void printElements(String prompt, String array[])
   {
      System.out.print(prompt + ": ");
      for (int idx = 0; idx < array.length; idx++)
         System.out.print(array[idx] + " ");
      System.out.println();
   }
} // end TestArrayParameters class
End example

Listing 6.20 has a main method that first declares an array called days and initializes it to a set of seven strings. Next, it prints the values as in the COBOL example to show that initialization worked as expected, by calling a little helper method called printElements, which is shown at the bottom of the class. Then, it calls another supplied method called changeArrayElements, which, as you can see, changes each element by filling the array with the string XXX.

To see if that affected the mainline array, printElements is called again. Then, the program calls another supplied method, changeArrayReference. This method changes not the elements this time, but the actual array variable itself, pointing it to an entirely new array filled with the string YYY in each element. If this affects the calling code, you would expect the last call to printElements to show all YYYs, since it is a new array. Here is the result of running this test case:

>javac TestArrayParameters.java
>java TestArrayParameters
before: mon tue wed thu fri sat sun
after element changes : XXX XXX XXX XXX XXX XXX XXX
after reference change: XXX XXX XXX XXX XXX XXX XXX

The call to changeArrayElements did change the contents of the array in the mainline code. The call to changeArrayReference had no affect on the mainline array variable; it still points to the same array in memory as it did before the call. Thus, you see that array contents can be changed, but array addresses cannot be changed. Again, the address of the array itself is passed by value.


Using Arrays Instead Of Primitives To Simulate Pass-By-Reference

Remember that primitive values like integers are always passed by value in Java. That means you cannot write a method to swap the values of two integer variables, for example. The swap will have no affect on the caller's values of those variables. Pass-by-value effectively means a copy of the parameter values is passed to the method. We see one possible circumvention for this: instead of passing primitive values, you could pass array objects—arrays with only one element, since array elements can be effectively changed by methods.

Figure 6.21, then, is a swap method that works in Java.

Listing 6.21: An example of a swap method in Java.
Start example
public class TestSwap
{
   public static void main(String args[])
   {
      int value1 = 10;
      int value2 = 20;
      int value1Array[] = new int[1];
      int value2Array[] = new int[1];
      value1Array[0] = value1;
      value2Array[0] = value2;
      System.out.println("value1 = " + value1 + ", value2 = " + value2);
      swap(value1Array, value2Array);
      value1 = value1Array[0];
      value2 = value2Array[0];
      System.out.println("value1 = " + value1 + ", value2 = " + value2);
   }
   public static void swap(int value1[], int value2[])
   {
      int temp = value1[0];
      value1[0] = value2[0];
      value2[0] = temp;
      return;
   }
} // end TestSwap class
End example

The important method here is swap, which takes two single-element arrays and swaps the first element of each. The trick to using this method with primitive values is to first move those values into the first element of a couple of arrays created for this purpose and then, after calling swap with those arrays, move the values back into the primitive variables from the arrays. This is shown in the main method of the example. While this is not a pretty thing to have to do, it does work, as shown in the following output:

>java TestSwap
value1 = 10, value2 = 20
value1 = 20, value2 = 10


Summary

In this chapter, you discovered various uses for arrays, vectors, and hashtables. The following are key points from this chapter:

  • Arrays, vectors, and strings are objects in Java.

  • Java has two kinds of arrays: compile-time and runtime.

  • Java supports multidimensional arrays and unbalanced arrays.

  • The main rule for unbalanced arrays is to define them left to right.

  • The Arrays class in java.util contains static methods to search, sort, and fill arrays.

  • Vectors in Java can shrink and grow on demand.

  • Hashtables in Java can store values indexed by a key, and then easily and quickly find a value given its key.

  • Parameters are passed by value in Java, whereas in COBOL they are passed by reference, by default.

  • To circumvent the pass-by-value problem for primitive type variables, you can use single-element arrays.


Chapter 7: String Manipulation

Overview

Chapter 5 discusses primitive data types, including the char (character) type. It mentions that you can create a string using an array of type char Java has a better way of dealing with strings, however: the String class in the java.lang package, which is always imported and hence always available to you. Throughout this book, examples use this String class, so we will discuss it in more detail here, and the methods supplied with it.

Why does Java use a class for strings instead of a built-in data type? Because built-in data types or primitives are restricted to the few operators built into the language for math and manipulation purposes (like addition and subtraction). So much time is spent preparing, converting to and from, manipulating, and querying strings, that you gain significant programmer productivity from a class (versus a built-in primitive data type), given all the methods that can be supplied with it for common string operations.

For example, how many times have you written justification, trimming, and conversion code for text strings? Why not have language-supplied methods to save you that drudgery? In COBOL, such language-supplied functionality is given in the form of statements and intrinsic functions. In an object-oriented language, it comes in the form of methods in a simple, self-contained class. Through string classes in object-oriented languages, you first start to appreciate the power and elegance of objects. This great ability to encapsulate useful methods or functions that are commonly needed by programmers into a class data type drives home the potential of objects and OO. For example, to support a new operation, you need only add a new method, versus designing a new operator into the language.


Java String Basics And Pitfalls

An important note about strings in Java is that the language designers relaxed their strict object-oriented rules slightly to allow strings to be concatenated directly with a built-in operator—the plus sign (+). They also allowed an intuitive means of instantiating strings that does not force you to use formal object instantiation syntax (as they did for compile-time arrays). This underlies the importance of strings and string manipulation to every programmer and their invariable prominence in every program.

The more intuitive and convenient it is to use strings in a language, the more accepted that language is by programmers. Certainly, Java's goal is to keep it simple. If you prefer to stick to the rules, however, that is acceptable, too. In other words, if you prefer to instantiate a string in the formal way and use a method call, concat, to concatenate two strings instead of the + operator, that is perfectly acceptable. For example, you can use an intuitive style like this:

String text1 =  "George";
String text2 =  "Phil";
String finalText = text1 + " and " + text2;
System.out.println(finalText);

You can also use a formal style like this:

String text1 = new String("George");
String text2 = new String("Phil");
String finalText = new String(text1);
finalText = finalText.concat(" and ");
finalText = finalText.concat(text2);
System.out.println(finalText);

The output of both examples is George and Phil, as you would expect. In these examples, you see that there are two ways to initialize strings: by implicitly equating them to string literals, or by explicitly allocating an instance of the String class using the new operator.

Once you create your strings, you can manipulate them using the many methods supplied with the String class. These samples also highlight the two means of adding strings together in Java, either via the intuitive + operator or the concat method of the String class. In the latter, the string passed as a parameter is appended to the string represented by the String object. The actual String target object is not affected; rather, a new object is created and returned. Thus, the method call has no side effects. Can you guess the output of the following?

String finalText = "George";
finalText.concat("and Phil");
System.out.println(finalText);

The answer is George, not George and Phil, as you might initially expect. Do not get caught by this common mistake.

Another important consideration is string equality in Java. You cannot use the equality operator (= =) to compare two String objects, as in the following:

if (text1 == text2)

Instead, you must use the method equals, which returns true if the target string and the passed-in string are equivalent, as follows:

if (text1.equals(text2))…

This is the single most common mistake when using the String class. The problem is that the use of natural instantiation and the plus operator for concatenation tend to make you think of strings as primitive data types in Java. However, they are actually objects of the String class—that is, they are object reference variables. Like all object reference variables, they actually contain a memory address of the class instance, and as such, the equality operator only tells you if the two variables refer to the same address, which they almost never do.

The operator does not have the intelligence to make an intelligent, "if all characters are the same and the strings are the same length" comparison. Rather, the code inside the equals method is required for this. As a COBOL programmer, you are even more prone to this pitfall than other programmers, because COBOL has a free-form IF syntax that does allow you to compare two strings (alphabetic data items) using the equality operator, as in this example:

IF (STRING1  =  STRING2) THEN

Take care in your Java coding to avoid this nasty bug, since you will not notice it for a while because the compiler will not complain about it. Equality testing of object references is legal, after all, for those cases when you want to know if two variables actually do refer to the same allocated instance in memory.


Strings In COBOL

COBOL does not have a pure string category. Instead, you can define a data item as a fixed-length character or alphanumeric data item:

WORKING-STORAGE SECTION.
01  MYSTRING       PIC X(20).
01  MYOTHERSTRING  PIC X(9) VALUE "Cassandra".

In this example, MYSTRING is defined as a 20-character fixed-length data item, and MYOTHERSTRING is defined as a nine-character fixed-length data item, initialized to an initial value of Cassandra using the VALUE keyword. By default, double quotes are used to delimit a character literal in COBOL, but you can use single quotes if you also use the APOST process option. (On OS/390, you can use single or double quotes whether or not you use the APOST process option.)

If you initialize a character data item to a value that is too small, it is simply padded on the right with blanks, to the declared length. To later assign a value to a character data item, you can use the INITIALIZE or MOVE statements. If the value assigned is too small, it is padded on the right; and if it is too big, it is truncated on the right:

000-MAIN.
    MOVE "Jennifer" TO MYSTRING.

These examples in COBOL are similar to declaring and instantiating a String object in Java. The main difference is that, in Java, you use methods supplied by the String class to manipulate the string object (for the most part), compared to the COBOL character data-manipulation statements and intrinsic functions supplied as part of the COBOL language.

Another big difference is you never specify a string length in Java. Instead, it is implicitly specified by the length of the value currently in the string, for example:

String myString = "Phil"; // new string, length four
MyString = "George"; // assigned a new string value, of length 6

The length of a string is exactly the length of its current contents, which can be changed with an assignment statement. The new value is never padded or truncated by Java. If you want padding with blanks, you have to specify that explicitly in your literal, as in this example:

String myString = "Phil    "; // new string, length eight

By the way, unlike COBOL's COMPUTE statement, Java's assignment statement can be used for both numeric and string variables. Take careful note of the double quotes around the literals. Unlike COBOL, you have no choice in Java; you have to use double quotes. What if your literal contains a double quote? Recall from chapter 5 that you have to use an escape sequence, in this case a backslash followed immediately (with no blanks) by the embedded quote, as in this example:

String myString = "Phil says \" do you get it? \"";

Aside from the hard-wired support for direct assignment without the new operator, and the + operator for concatenation, the remainder of Java's string functionality comes from methods in the String class. As you will see, Java has many more methods to facilitate string manipulation than COBOL has statements.

Table 7.1 compares all available string-manipulation statements in COBOL to those methods available in Java. Java offers more functionality than is shown here; you will find out about that additional functionality shortly.

Table 7.1: COBOL Versus Java String-Manipulation Statements.
COBOL Statement | COBOL Intrinsic | Description | Java Method(s)
STRING |  | Concatenate two or more strings. | concat(string) or + operator
MOVE (start : length) |  | Extract a substring from a string. | substring(int start, int end) or substring(int start)
INSPECT data-item TALLYING counter FOR ALL string |  | Scan for a substring. | indexOf()
INSPECT TALLYING LEADING plus MOVE |  | Trim leading blanks. | trim()—Replaces both leading and trailing blanks.
 | LENGTH | Return length of string. | length()
INSPECT data-item REPLACING ALL from-char BY to-char |  | Translate all occurrences of "from" character to "by" character. | replace(char replaceChar)
INSPECT data-item CONVERTING from TO to | LOWER-CASE and UPPER-CASE | Translate "from" characters to "to" characters. | No INSPECT equiv-alent, but there are toLowerCase() and toUpperCase() methods in the String class.
IF data-item IS <NOT> NUMERIC |  | Determine if the data item contains only numeric digits, 0 through 9. | isDigit() in the Character class. Note that this works only on in dividual characters, not strings.
 | NUMVAL | Parse an alphanu-meric string with sign and decimal characters into a numeric value. | BigDecimal—Class constructor that takes a String argument
 | REVERSE | Reverse a given alphanumeric string. | reverse method in StringBuffer
UNSTRING |  | Split a given string into substrings by delimiter. | StringTokenizer class


String Manipulation

The next few sections examine each of the operations listed in Table 7.1 and give examples of them for both COBOL and Java. In the case where there is no matching method in Java, such as INSPECT CONVERTING, you learn how to write a method yourself to simulate the function.

Concatenation

We start with string concatenation for both COBOL and Java, looking at an example to illustrate the use of this function. Suppose you have two data items: a person's first and last name. You need to concatenate these and print out the result. This is easy to do in both languages, since both have support for concatenating strings. In COBOL, you use the STRING statement, as shown in Listing 7.1.

Listing 7.1: An example of the STRING statement in COBOL.
Start example
01 NAME-ONE.
   05 NAME1-FIRST PIC X(4) VALUE "Phil".
   05 FILLER      PIC X    VALUE SPACE.
   05 NAME1-LAST  PIC X(9) VALUE "Coulthard".
01 NAME-TWO.
   05 NAME2-FIRST PIC X(6) VALUE "George".
   05 FILLER      PIC X    VALUE SPACE.
   05 NAME2-LAST  PIC X(4) VALUE "Farr".
01 NAME-COMBINED PIC X(15) VALUE SPACES.
PROCEDURE DIVISION.
000-MAIN.
   STRING
      NAME1-FIRST DELIMITED BY SPACE
      " and "     DELIMITED BY SIZE
      NAME2-FIRST DELIMITED BY SPACE
      INTO NAME-COMBINED.
   DISPLAY NAME-COMBINED.
   STOP RUN.
End example

This example uses two data items, NAME1-FIRST to represent the first name, and

NAME1-LAST to represent the last name. These data items are declared and initialized right in WORKING-STORAGE. In more complex applications, these data items might be read from the screen or from a file on disk. They can even be passed in via the command line or a parameter. The PROCEDURE DIVISION uses the STRING statement to concatenate data item NAME1-FIRST to data item NAME2-FIRST and insert the string " and " between the two.

The result of the concatenation is placed in the data item called NAME-COMBINED, which is declared in WORKING-STORAGE as well, and luckily is exactly the required length. If it were too short, you would have truncation. If it were too long, you would have blank padding on the right. The DISPLAY statement displays the value of NAME-COMBINED, which is Phil and George, as you would expect.

Listing 7.2 illustrates the same example, written in Java. In this case, the String object is used to declare all the string variables, namely first1, last1, first2, last2, and combinedName. As discussed earlier, by declaring the string data items and initializing them, they are both created and initialized, that is, no new keyword is required to instantiate the object. Then, you can use the concat method to append " and " to first1, and then add first2 to the result of that first concatenation.

Notice that first1 is used as the object in the call to the concat method, while " and " is used as the parameter to concat. This results in a new temporary object, on which concat is called again and passed first2 as the parameter. The result of the operation is placed in the combinedName variable and displayed using the println method.

Listing 7.2: An example of string manipulation in Java.
Start example
public static void main(String args[])
{
   String first1 = "Phil";
   String last1 = "Coulthard";
   String first2 = "George";
   String last2 = "Farr";
   String combinedName;
   combinedName = first1.concat(" and ").concat(first2);
   System.out.println("The combined name is: " + combinedName);
}
End example

Did you notice another concatenation in this example? If so, you have a sharp eye. The println method concatenates the string literal "The name is :" to the object reference variable name using the + operator. This is another way of concatenating two strings in Java. The following replaces the concat Java method call with the + operator:

combinedName = first1 + " and " + first2;

Clearly, the use of the plus operator is a plus for programmers!

Substring

Next, let's take a look at the substring operation in COBOL, and the corresponding substring method in Java. In COBOL, you use the MOVE statement to extract a substring from a string starting at a specified location for a specified length, as in the example in Listing 7.3.

Listing 7.3: An example of a substring operation in COBOL.
Start example
WORKING-STORAGE SECTION.
01 WORD1 PIC X(4).
01 WORD2 PIC X(5).
*                                 12345678901234
01 INPUT-STRING PIC X(20) VALUE "Java For COBOL".
PROCEDURE DIVISION.
000-MAIN.
   MOVE INPUT-STRING (1:4) TO WORD1.
   MOVE INPUT-STRING (10:5) TO WORD2.
   DISPLAY WORD1.
   DISPLAY WORD1.
   STOP RUN.
End example

Listing 7.3 starts with an alphanumeric data item, INPUT-STRING, which contains the string Java for COBOL. It uses the indexed version of MOVE to extract out of that data item the substrings, starting at character position 1 for a length of four, and starting at character position 10 for a length of five. This gives the results in data items WORD1 and WORD2, which the DISPLAY statements confirm hold Java and COBOL.

Listing 7.4 shows how this example is written in Java, using the String method substring. This Java version declares two String object reference variables, word1 and word2, which will hold the substrings. It also declares and initializes the input string variable, inputString. It then uses the substring method to first extract the word starting at character position 0 and ending at position 3, and then to extract the word starting at character position 9 and extending to the end of the input string.

Listing 7.4: An example of the substring method in Java.
Start example
String word1;
String word2;
//                    01234567890123
String inputString = "Java for COBOL";
word1 = inputString.substring(0,4);
word2 = inputString.substring(9);
System.out.println(word1);
System.out.println(word2);
End example

Clearly, there are a few of subtle differences versus COBOL:

  • The character positions are zero-based, not one-based.

  • The second parameter is not the length, but rather the ending position. Further, it is the ending position plus one. Watch out for that!

  • The second parameter is optional, and if not specified, implies "go to the end of the input string."

Otherwise, the logic is similar to COBOL's, and is easy enough to follow.

Why does the second parameter have to be one past the actual ending column? This actually makes some processing easier. For example, you don't normally know exactly what column to start the substring operation in, and what column to end it in. Instead, you can determine this programmatically by searching for specific delimiting characters, such as a blank, comma, or dollar sign. This is done in Java using the indexOf method that we'll discuss shortly, but it is very simple to use—you give it the character to find, and it re- turns the zero-based position of that character (and you can specify what position to start the search).

When searching for the ending delimiter, you will subsequently be substringing up to the character position right before that delimiter, so this funny rule of Java's substring method makes it a little easier. For example, you could have written the first substring example from Listing 7.4 as this:

word1 = inputString.substring(0,inputString.indexOf(' '));

Before going into more of the String methods, let's stop for a minute and discuss some- thing you might be thinking. In COBOL, it is common practice to define a group item for names, where the sub-group data items break the name down into first, middle, and last names. Since Java does not have structures, how might you do the equivalent? If you an- swered "write a class," you deserve a pat on the back. (Go ahead, give yourself one.) This is perhaps the first class you will write if you have to deal with names in your application. Listing 7.5 gives an example.

Listing 7.5: Using a class to define a group item.
Start example
/**
* Class representing a person's name
* @author Phil Coulthard
*/
public class Name
{
   private String firstName;
   private String middleName;
   private String lastName;

   /**
   * Constructor one: given all three parts of the name
   */
   public Name(String first, String middle, String last)
   {
      firstName = first;
      middleName = middle;
      lastName = last;
   }
   /**
   * Constructor two: given only first and last parts
   */
   public Name(String first, String last)
   {
      this(first, "", last);
   }
   /**
   * Constructor three: given only first part
   */
   public Name(String first)
   {
      this(first, "", "");
   }

   // methods...
   public String getFirstName()
   {
      return firstName;
   }
   public String getMiddleName()
   {
      return middleName;
   }
   public String getLastName()
   {
      return lastName;
   }
   public String toString()
   {
      String fullname = firstName;
      if (!middleName.equals(""))
         fullname = fullname + " " + middleName;
      if (!lastName.equals(""))
         fullname = fullname + " " + lastName;
      return fullname;
   }
} // end of class Name
End example

The Name class defines three private instance variables, firstName, middleName, and lastName. The assumption is that users who create Name objects will give the constructor these three name parts as separate parameters, which are then assigned to these instance variables. However, since some people have only one or two names instead of three, the class has constructors to handle these cases as well. These constructors assign "#" to the unused name parts. This is the null string, and is the preferred placeholder when there is no string. The only other option is the actual keyword null, but that would give a runtime error when you use variables that are null.

Now that you can create name objects that hold the three sub-names, you need to do something with them. Since the instance variables are private, which is good style, you have to supply methods to return the first, middle, and last names, as shown in getFirstName, getMiddleName, and getLastName. Also, the toString method returns the full name concatenated with blanks as appropriate. Remember to use the equals method to compare strings, not the = = operator.

Note

Although you could call this toString method anything, such as getFullName, it is always a good idea to supply a toString method that returns some meaningful string, since System.out.println calls toString when it is given an object.

To test this little helper class, a main method instantiates some instances of the class, test- ing each of the three constructors, and printing out the results to verify them:

public static void main(String args[])
{
   Name name1 = new Name("Phil", "Stephen", "Coulthard");
   Name name2 = new Name("George", "Farr");
   Name name3 = new Name("Cher");
   System.out.println(); // start with new line
   System.out.println(name1);
   System.out.println(name2);
   System.out.println(name3);
}

The result of compiling and running this is the following:

Phil Stephen Coulthard
George Farr
Cher

As you can see, it works, and the toString method is called by System.out.println. This is a nice example of the elegance of objects. You create one little class, and then create as many objects in your code from it as you need, each with its own data. Each object supports all the methods defined for that class. You can even come back later and add new methods as necessary, such as getFullNameWithMiddleInitial, and all objects will get that new method. Another good candidate for a new method would be parseFullName to take a full name like "George Neimann Farr" and parse it into the three name parts, populating the instance variables.

Searching for a Substring

One of the more commonly used functions in almost all languages is the ability to search one string for the occurrence of another. For example, you might have a string like "Java For COBOL Programmers," and you want a count of the character a. In COBOL, you can determine this using the INSPECT TALLYING statement, as Listing 7.6 illustrates.

Listing 7.6: Searching one string for the occurrence of another in COBOL.
Start example
WORKING-STORAGE SECTION.
01 A-COUNTER PIC 999 VALUE 0.
01 INPUT-STRING PIC X(30) VALUE "Java For COBOL Programmers".
PROCEDURE DIVISION.
000-MAIN.
   INSPECT INPUT-STRING TALLYING A-COUNTER FOR ALL "a".
   DISPLAY A-COUNTER.
End example

In this example, a string data item called INPUT-STRING is initialized to "Java For COBOL Programmers." The total number of occurrences of the letter a in the data item is placed in the A-COUNTER data item. When the example is run the result, 3, is displayed. COBOL's INSPECT TALLYING only returns the count. It does not return the position of the found character or characters searched for. To find the actual position so you can perform a subsequent substring with, for example, MOVE, use the POINTER phrase of UNSTRING. Alternatively, you can do that search yourself, as shown in Listing 7.7.

Listing 7.7: Finding the position of a substring within a string.
Start example
WORKING-STORAGE SECTION.
01 SEARCH-PARMS.
   05 INPUT-STRING PIC X(30) VALUE SPACES.
   05 INPUT-LEN PIC 999 VALUE 30.
   05 SEARCH-STRING PIC X(1) VALUE SPACE.
   05 SEARCH-LEN PIC 999 VALUE 1.
   05 NEXT-OCCURRENCE PIC 999 VALUE 0.
   05 PREV-OCCURRENCE PIC 999 VALUE 0.
   05 CHAR-INDEX PIC 999 VALUE 0.
PROCEDURE DIVISION.
000-MAIN.
*    123457678901234567890123456
   MOVE "Java For COBOL Programmers" TO INPUT-STRING.
   MOVE "a" TO SEARCH-STRING.
   PERFORM FIND-NEXT-STRING WITH TEST AFTER
      UNTIL NEXT-OCCURRENCE = 0.
   STOP RUN.
FIND-NEXT-STRING.
   COMPUTE NEXT-OCCURRENCE = PREV-OCCURRENCE.
   PERFORM WITH TEST BEFORE
      UNTIL NEXT-OCCURRENCE NOT EQUAL PREV-OCCURRENCE

      COMPUTE CHAR-INDEX = CHAR-INDEX + 1
      IF (CHAR-INDEX > INPUT-LEN) THEN
         COMPUTE PREV-OCCURRENCE = 0
      ELSE
         IF (INPUT-STRING (CHAR-INDEX:SEARCH-LEN) = SEARCH-STRING)
            THEN
               COMPUTE NEXT-OCCURRENCE = CHAR-INDEX
         END-IF
      END-IF
END-PERFORM.
DISPLAY NEXT-OCCURRENCE.
End example

Listing 7.7 defines a paragraph called FIND-NEXT-STRING that is designed to be called repetitively on a given input string called INPUT-STRING until all occurrences of the given substring, SEARCH-STRING, have been found. It searches from the previous match position, PREV-OCCURRENCE, until it finds another match or reaches the end of the input string, in which case it returns zero. The group item SEARCH-PARMS defines all of the input parameters that callers are required to set and all of the output parameters that it returns, namely PREV-OCCURRENCE and NEXT-OCCURRENCE. The results of running the example in Listing 7.7 are 2, 4, and 20 before 0 is shown, indicating that no more matches are found.

In Java, searching for a substring and getting its position is a simple operation using the indexOf method in the String class, as shown in Listing 7.8. The indexOf method takes one or two parameters. The first one is the string you are looking for, and the optional second is the start location of the search (the character position). Again, this is a zero-based position, not a one-based position as in COBOL. If you do not specify the start location, as with COBOL, the default start value will be set to the first character position (that is, zero). In this example, the value that is printed is 1 (zero-based, again, so 1 is the second character). The indexOf method returns –1 if the given substring is not found in the input string.

Listing 7.8: Determining the position of a substring within a string in Java.
Start example
public static void main(String args[])
{
   String inputString = "Java For COBOL Programmers";
  int findPosition = inputString.indexOf("a");
  System.out.println(findPosition);
} // end main
End example

Note that Java's indexOf method does not count the occurrences of the given search string (yes, you can pass a string, not just a single character), like COBOL's INSPECT TALLYING does. If you wanted a count, you would just loop until you got –1 back from indexOf, counting the occurrences as you went, and specifying the previous match position plus one as the second parameter on each call, as in the following:

int counter = 0;
int findPos = inputString.indexOf("a");
while (findPos != -1)
{
   counter++;
   findPos = inputString.indexOf("a",findPos+1);
}
System.out.println(counter);

In addition to indexOf, Java also supplies a handy lastIndexOf method, which searches backwards for a given substring. Again, it has an optional second parameter for specifying where to start the search, but this time the search continues backward from that start position.

Finally, both indexOf and lastIndexOf support either a string parameter as you have seen, or a single-character parameter when searching for an individual character, as in the following:

int dollarPos = myString.indexOf('$');

Trimming Blanks

Trimming is the process of removing leading or trailing blanks from a string. Both COBOL and Java have built-in support for trimming. However, since there is no simple statement or intrinsic function in COBOL for this, we are forced to use our own.

Suppose you had the string " Java For COBOL Programmers " with blanks at the beginning and the end. How would you strip them off, in COBOL? Listing 7.9 shows one possible way.

Listing 7.9: Trimming blanks in COBOL.
Start example
WORKING-STORAGE SECTION.
01 TRIM-PARMS.
   05 INPUT-STRING    PIC X(30) VALUE SPACES.
   05 OUTPUT-STRING   PIC X(30) VALUE SPACES.
   05 INPUT-LEN       PIC 999 VALUE 30.
   05 OUTPUT-LEN      PIC 999 VALUE 30.
   05 LEADING-BLANKS  PIC 999 VALUE ZERO.
   05 STARTING-POS    PIC 999 VALUE ZERO.
   05 FINAL-LEN       PIC 999 VALUE ZERO.
PROCEDURE DIVISION.
000-MAIN.
*      123456789012345678901234567890
   MOVE "  Java For COBOL Programmers  " TO INPUT-STRING.
   PERFORM TRIM-LEADING.
   DISPLAY OUTPUT-STRING.
   STOP RUN.
TRIM-LEADING.
   INSPECT INPUT-STRING TALLYING LEADING-BLANKS FOR LEADING " ".
   COMPUTE STARTING-POS = LEADING-BLANKS + 1.
   COMPUTE FINAL-LEN    = INPUT-LEN - LEADING-BLANKS.
   MOVE INPUT-STRING (STARTING-POS:FINAL-LEN) TO OUTPUT-STRING.
End example

The paragraph TRIM-LEADING in Listing 7.9, together with its input parameters in group item TRIM-PARMS, will trim leading blanks off the given string INPUT-STRING. This is done by first using INSPECT TALLYING to count the number of leading blanks, then using this to determine the starting position of a MOVE with substringing notation. This listing doesn't bother trimming trailing blanks because using MOVE to populate the output-string data item pads the output string on the right with blanks, anyway. So, the result of running Listing 7.9 is "Java For COBOL Programmers ".

In Java, the task of trimming blanks, both leading and trailing, is done using the appropriately named trim method of the String class, as shown in Listing 7.10. In this case, the result is "Java For COBOL Programmers", with all leading and trailing blanks removed.

Listing 7.10: Trimming blanks in Java.
Start example
public class Trim
{
   public static void main(String args[])
   {
      String inputString = "  Java For COBOL Programmers  ";
      String outputString;
      outputString = inputString.trim();
      System.out.println("\"" + outputString + "\"");
   }
} // end class Trim
End example

Easy stuff. However, what if you only want to remove leading blanks? Or trailing blanks? In COBOL, it is quite easy to at least remove leading blanks, as you saw, using a combination of INSPECT TALLYING and MOVE with subscripting. Java, on the other hand, only has the trim() method, and unfortunately, no trimLeading or trimTrailing meth- ods. It is not brain surgery or VCR programming to write your own code to do this, however, and you will do so by the end of this chapter, after you have learned about another class, called StringBuffer.

Determining String Lengths

Determining the length of a string, to decide if it is empty or needs truncation or padding, is a simple task in both languages. In COBOL, you can simply use the LENGTH intrinsic function, specifying a data item or string literal as a parameter. An example is shown in Listing 7.11.

Listing 7.11: Determining string length in COBOL.
Start example
WORKING-STORAGE SECTION.
01 TRIM-PARMS.
   05 INPUT-STRING PIC X(30) VALUE SPACES.
   05 OUTPUT-STRING PIC X(30) VALUE SPACES.
   05 INPUT-LEN PIC 999 VALUE 30.
   05 OUTPUT-LEN PIC 999 VALUE 30.
   05 LEADING-BLANKS PIC 999 VALUE ZERO.
   05 STARTING-POS PIC 999 VALUE ZERO.
   05 FINAL-LEN PIC 999 VALUE ZERO.
PROCEDURE DIVISION.
000-MAIN.
*          123456789012345678901234567890
   MOVE " Java For COBOL Programmers " TO INPUT-STRING.
   PERFORM TRIM-LEADING.
   DISPLAY FUNCTION LENGTH (INPUT-STRING).
   DISPLAY FUNCTION LENGTH (OUTPUT-STRING).
   STOP RUN.
TRIM-LEADING.
   INSPECT INPUT-STRING TALLYING LEADING-BLANKS FOR LEADING " ".
   COMPUTE STARTING-POS = LEADING-BLANKS + 1.
   COMPUTE FINAL-LEN = INPUT-LEN - LEADING-BLANKS.
   MOVE INPUT-STRING (STARTING-POS:FINAL-LEN) TO OUTPUT-STRING.
End example

The example in Listing 7.11 builds on Listing 7.10, adding two lines (shown in bold) to display the lengths of INPUT-STRING and OUTPUT-STRING. You might expect to see the numbers 30 and 26 as output, but you actually see 30 and 30 because the LENGTH intrin- sic function gives the declared length of fixed-size character data items.

In Java, again with a String object, you invoke the length method to return an integer indicating the length of the string, or number of characters, as shown in Listing 7.12. In this case, the numbers 30 and 26 are actually the output because String objects in Java are only as long as their actual contents, versus some hard-coded fixed length.

Listing 7.12: Determining the length of a string in Java.
Start example
public class Length
{
   public static void main(String args[])
   {
      String inputString = "  Java For COBOL Programmers  ";
      String outputString;
      outputString = inputString.trim();
      System.out.println(inputString.length());
      System.out.println(outputString.length());
   }
} // end class Length
End example

Remember that in Java, characters are two bytes long because they are Unicode characters. The length returned by the length method is the number of characters, not the number of bytes—the latter is actually two times the former. All String class methods that take or return an index number deal with the character position instead of the byte offset, however, so you rarely need to worry about the fact that characters are two bytes long.

Translating Characters

You might sometimes need to replace all occurrences of one character with another. Perhaps you are replacing dollar signs with local currency symbols, or replacing all slashes in a date format with a period. To do this in COBOL, you can use the INSPECT REPLACING ALL statement. This replaces all occurrences of a given character with another given character in the given alphanumeric data item:

INSPECT date-variable REPLACING ALL "/" BY ".".

This is also easily done in Java. You simply use the replace method in the String class, specifying the character to be replaced and the character to replace with:

dateVariable.replace('/', '.');

This replaces all occurrences of the slash character with the period character, just as in the COBOL example. Unlike COBOL's INSPECT REPLACING statement, this is the only variation of the replace method in Java. There are no other options, such as only replace leading occurrences.

Translating Strings

So far, we have compared COBOL statements to available Java methods. As you saw in Table 7.1, there are a few COBOL statements or intrinsic functions that are simply not available in Java. One is the INSPECT CONVERTING statement in COBOL, which converts not just characters but whole substrings, and which has no apparent equivalent in Java. What can you do in this case? Write your own method!

First, let's review the COBOL support. Listing 7.13 shows an example of the COBOL INSPECT CONVERTING statement.

Listing 7.13: COBOL's INSPECT CONVERTING statement.
Start example
WORKING-STORAGE SECTION.
01 UPPER-CASE PIC X(26) VALUE "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
01 LOWER-CASE PIC X(26) VALUE "abcdefghijklmnopqrstuvwxyz".
01 INPUT-STRING PIC X(26) VALUE "Java For COBOL Programmers".
01 OUTPUT-STRING-UC PIC X(26).
01 OUTPUT-STRING-LC PIC X(26).
PROCEDURE DIVISION.
000-MAIN.
   MOVE INPUT-STRING TO OUTPUT-STRING-UC.
   MOVE INPUT-STRING TO OUTPUT-STRING-LC.
   INSPECT OUTPUT-STRING-UC
            CONVERTING LOWER-CASE TO UPPER-CASE.
   INSPECT OUTPUT-STRING-LC
            CONVERTING UPPER-CASE TO LOWER-CASE.
   DISPLAY OUTPUT-STRING-LC.
   DISPLAY OUTPUT-STRING-UC.
   STOP RUN.
End example

The example in Listing 7.13 shows a typical usage of INSPECT CONVERTING to convert alphanumeric data items to all uppercase or all lowercase. This statement can be used for all types of purposes whenever characters need to be replaced with alternative characters. It is basically identical to a series of INSPECT REPLACING statements—in this case, 26 such statements.

Java has no corresponding method in its String class, but it does have a related method called replace, which takes two character parameters as input and replaces all occurrences of the first character in the target string object with the second. It sounds similar to COBOL's INSPECT CONVERTING, except that replace only replaces a single character, not multiple characters. Not to worry; you can write your own Java method that emulates COBOL's support by using the replace method repeatedly, once for each character in a given string of characters.

Note that you cannot extend the String class (as discussed in chapter 9) because the Java language designers made it final, preventing this. Thus, String augmentation methods are created as traditional, stand-alone functions—that is, they are defined as static, and take as parameters whatever they need. Since even static methods must exist in a class, we'll create an arbitrary class called OurStringMethods. Listing 7.14, then, is the Java equivalent to COBOL's INSPECT CONVERTING statement. (For consistency, the method is named converting.)

Listing 7.14: A Java method that is equivalent to COBOL's INSPECT CONVERTING statement.
Start example
public class OurStringMethods
{
   public static String converting(String source,  String fromChars,
                                    String toChars, int    start)
   {
      String resultString;
      // minimal input error checking
      if (fromChars.length() != toChars.length())
         return new String("BAD INPUT!");
      if (start > source.length() || start < 0)
         return new String("BAD INPUT!");
      // first off, get the substring to be xlated...
      resultString = source.substring(start);
      // now, xlate each char in fromChars to same pos in toChars
      for (int i = 0; i < fromChars.length(); i++)
         resultString = resultString.replace(fromChars.charAt(i),
                                             toChars.charAt(i));
      // now append xlated part to non-xlated part
      resultString = source.substring(0,start) + resultString;
      return resultString;
   } // end converting method
   public static String converting(String source,  String fromChars,
                                    String toChars)
   {
      return converting(source, fromChars, toChars, 0);
   } // end converting method two
} // end OurStringMethods class
End example

The required parameters of the converting method are the source string to be translated, followed by the "from" and "to" strings, and finally the start position, where the translation should start. To be consistent with Java's string methods, this start position is zero-based. To be consistent with COBOL, the start position value should be optional, defaulting to the first character if not passed.

To support an optional parameter at the end of the parameter list in a Java method, you simply supply a second method with the same name that does not specify or accept that last parameter, as you can see in the example. This second, overloaded method can simply call the first full version of the method and pass in the default value for the missing parameter. Recall that overloaded means that this method has the same name as another method in the same class, but takes a different number of parameters—that is, it has a different signature.

The code in the first and primary converting method is reasonably straightforward—after making sure the input is valid, a substring of the source excludes the characters before the given start position. Next, for every character in the "from" string, the String replace method replaces all occurrences of that character with the character in the corresponding position of the "to" string. Finally, this is appended to the substring of the source up to the start position, and the resulting string is returned.

Note

In order to get an individual character out of a string, you must use the charAt method and supply the zero-based index of the character you want.

To test this code, a main method is supplied so that you can call it from the command line and see the results. (This idea of supplying a main method for test cases of handwritten classes is a good idea, by the way.) Here, then, is the main method to test both versions of the method—first without specifying a start position, and then with specifying a start position:

public static void main(String args[])
{
   //            "012345678901234567890";
   String src  = "CBLB is for you Juys!";
   String from = "CBL";
   String to   = "Jav";
   System.out.println();
   System.out.println("Input string  : \"" + src + "\"");
   src = OurStringMethods.converting(src, from, to);
   System.out.println("Output string1: \"" + src + "\"");
   from = "J";
   to   = "G";
   src = OurStringMethods.converting(src, from, to, 16);
   System.out.println("Output string2: \"" + src + "\"");
}

The example translates the characters in CBL to the corresponding characters in Jav, then translates the character J to the character G, starting at position 16 (again, zero-based). The final result, after compiling both classes and then running from the command line, is the following:

Input string  : "CBLB is for you Juys!"
Output string1: "Java is for you Juys!"
Output string2: "Java is for you Guys!"

Translating Case

The COBOL example of INSPECT CONVERTING in Listing 7.13 used this statement to convert an alphanumeric data item's value to uppercase and to lowercase. Although this is a popular use of this statement, there is also an alternative. Specifically, intrinsic functions in COBOL make this easier to do, as shown in Listing 7.15.

Listing 7.15: An alternative to the INSPECT CONVERTING statement in COBOL.
Start example
WORKING-STORAGE SECTION.
01 INPUT-STRING PIC X(26) VALUE "Java For COBOL Programmers".
01 OUTPUT-STRING-UC PIC X(26).
01 OUTPUT-STRING-LC PIC X(26).
PROCEDURE DIVISION.
000-MAIN.
   MOVE FUNCTION UPPER-CASE (INPUT-STRING) TO OUTPUT-STRING-UC.
   MOVE FUNCTION LOWER-CASE (INPUT-STRING) TO OUTPUT-STRING-LC.
   DISPLAY OUTPUT-STRING-LC.
   DISPLAY OUTPUT-STRING-UC.
   STOP RUN.
End example

This example defines INPUT-STRING and two other alphanumeric data items, OUTPUT-STRING-UC and OUTPUT-STRING-LC, to hold the uppercase and lowercase versions of INPUT-STRING, respectively. It then uses the UPPER-CASE and LOWER-CASE intrinsic functions, right in the MOVE statements, to convert INPUT-STRING first to uppercase and then to lowercase, placing the results of the intrinsic function calls into the target output-string data items. Displaying the results gives the following:

"JAVA FOR COBOL PROGRAMMERS"
"java for cobol programmers"

Java also has intuitive language-supplied methods to do this, in the String class. The first method is toUpperCase, which translates the target string to all-uppercase. The second is toLowerCase, which translates the target string to all-lowercase. The Java equivalent of Listing 7.15 is shown in Listing 7.16.

Listing 7.16: Converting case in Java.
Start example
public class Uppercase
{
   public static void main(String args[])
   {
      String str = new String("Java for COBOL Programmers");
      System.out.println();
      str = str.toUpperCase();
      System.out.println("String in uppercase: " + str);
      str = str.toLowerCase();
      System.out.println("String in lowercase: " + str);
   }
}
End example

Compiling and running the example in Listing 7.16 results in the following:

String in uppercase: JAVA FOR COBOL PROGRAMMERS
String in lowercase: java for cobol programmers

A great benefit of the Java methods for converting case is that they handle international characters, such as those containing an umlaut. For example, ö becomes Ö. Because Java characters are Unicode-based, they inherently support international characters.

Class Condition

COBOL has a nice test to determine if the value of a given alphanumeric data item contains only numeric or alphabetic characters. This is the class condition test, as shown in Listing 7.17.

Listing 7.17: Checking the validity of input with the class condition test.
Start example
WORKING-STORAGE SECTION.
01 INPUT-STRING-1 PIC X(10) VALUE "abcdefghij".
01 INPUT-STRING-2 PIC X(10) VALUE "1234567890".
PROCEDURE DIVISION.
000-MAIN.
   IF (INPUT-STRING-1 IS ALPHABETIC) THEN
      DISPLAY "YES, IT IS ALPHABETIC".
   IF (INPUT-STRING-2 IS NUMERIC) THEN
      DISPLAY "YES, IT IS NUMERIC".
   STOP RUN.
End example

This is a handy way to test that a user-supplied value is valid for its category. For exam- ple, you can easily verify that data entered in an entry field is all digits. There is no single method like this in Java, but there is a method in the Character class called isDigit that might help. Recall that the Character class is the class wrapper of the char primitive data type. It has many useful helper methods such as isDigit that you are encouraged to try. The static isDigit method, as shown in Listing 7.18, takes a single primitive character as input, and returns true if that character is a digit from zero to nine, and false otherwise.

Listing 7.18: Validity testing in Java.
Start example
public static boolean isNumeric(String inputString)
{
   boolean allNumeric = true;
   for (int idx=0; idx<inputString.length() && allNumeric; idx++)
      if (!Character.isDigit(inputString.charAt(idx)))
         allNumeric = false;
   return allNumeric;
}
End example

This static helper method takes a string as input and loops through its characters using the isDigit method. It loops until the end of the string, or until isDigit returns false. The helper method returns true if the given string contains all digits, and false if it does not. Here is a quick test of it:

String inputString1 = "abcdefghij";
String inputString2 = "0123456789";
System.out.println(OurStringMethods.isNumeric(inputString1));
System.out.println(OurStringMethods.isNumeric(inputString2));

As you might expect, the results are false and then true. This method is worth including in your OurStringMethods class of helper String methods.

Parsing Strings

Another function in COBOL related to the class test is the NUMVAL intrinsic function. It takes an alphanumeric data item or literal like " –123456.78" and produces from it the raw numeric value 12345678. It is a great way to parse a string typed in by the user and quickly convert it to its numeric value, as follows:

COMPUTE numericValue = FUNCTION NUMVAL (" –123456.78").

There is no direct equivalent to this in Java. The closest thing we have is one of the constructors of the BigDecimal class, which is the Java class for working with decimal numerics. It has a constructor that takes a string argument, and similar to COBOL's NUMVAL function, parses the string, internally populates its precision (length) and scale (number of decimals) instance variables, and sets the value of the number. Here is an example:

BigDecimal numericValue = new BigDecimal(" –123456.78");

After conversion, you can work with and manipulate the BigDecimal object. You should find this sufficient for most of your parsing needs, but if it is not, you can always write your own parsing method, especially after you learn about the StringBuffer class in the next section.


Performance Considerations: The Stringbuffer Class

Recall the discussion at the beginning of this chapter about the concat method, and how it does not affect the String object on which you invoke it, but rather returns a new String object. You have seen that this is also true of other string-manipulation methods, like toUpperCase and replace. This is because the String class is immutable. That is, you cannot change a String object; you can only use its methods, all of which return new String objects. In many cases, the original String object is no longer used and is swept up later by the garbage collector.

This read-only behavior of strings can have performance implications for calculations that do a lot of string manipulations, such as any code that builds up a string by concatenating characters inside a loop. For this reason, Java supplies a second class called StringBuffer that is mutable. That is, it can be changed directly using supplied methods. This class is completely independent of the String class. Although some methods are common between the two, StringBuffer also has its own unique set of methods for altering the object directly, which you will shortly explore.

If you need to dynamically change the strings in your method, you should use

StringBuffer instead of String. Both classes support methods to convert back and forth between them. For example, you can use a StringBuffer object to do your string manipulation, and then, once the string is complete, convert it back to a String object using the toString method supplied in StringBuffer. This conversion back and forth between the String and StringBuffer classes has the added advantage of allowing you to use methods available in both classes by simply converting from one class to the other.

You will almost always want to accept and return String objects from your methods, since that is what your programmer-users will expect. The conversion to and from a StringBuffer is often done at the beginning and end of your String manipulation methods. For example, methods for significant string manipulations might follow this format:

public String workOnString(String input)
{
   StringBuffer workString = new StringBuffer(input);
   // do manipulation work on the workString variable
   return workString.toString();
}

COBOL's alphanumeric data items are fixed in size, but you can at least change the contents of a COBOL character data item, if the length is not affected. Even this is not possible in Java's String class. There are no methods in the class for replacing specific characters at specific positions. All Java has is the replace method, and like all the methods, it returns an entirely new String object that has the affected characters replaced. By contrast, the StringBuffer class allows all manner of manipulation on the string contents.

To declare a string using the StringBuffer class, you must use the formal way, with the new operator, optionally specifying a literal or String object as input, as follows:

StringBuffer aName = new StringBuffer("Angelica Farr");

There are no language extensions to allow intuitive instantiation like this:

= "this is a string"

as there are for String objects. Similarly, there are no language extensions for easy concatenation of StringBuffer objects using the plus sign, as there are for String objects. (Mind you, a plus sign is allowed between StringBuffer objects inside the System. out.println parameter, as it is for all data types. It simply invokes the toString method on the StringBuffer object, as it does for all objects it encounters.) To concatenate strings to a StringBuffer object, use the append method:

StringBuffer quotedName = new StringBuffer("George");
quotedName.append(" and ").append("Phil");

Notice how this method does have a side effect on the object it works against, so you do not need to equate the result to another variable as you would with the concat method in the String class. This common method returns the current StringBuffer object so you can string together multiple method calls in one statement, as shown here. Conveniently, the append method also has many overridden versions, supporting all the primitive data types as the parameter, with conversion to a string literal done for you. Consider this example:

boolean flag = true;
StringBuffer output = new StringBuffer("flag value = ").append(flag);
System.out.println(output); // results in "flag value = true"

This results in the output flag value = true. The append method also accepts String objects as input. In fact, it will accept any object as input. For objects, it simply calls the object's toString method to convert it to a string value.

You do not always want to change your string by appending to it. Sometimes, you want to insert new strings into the middle of it. The StringBuffer class supports this with an insert method, with a number of overridden methods similar to append, allowing all manner of data types to be inserted after being converted to String format. All versions of the insert method take an integer insertion-point index (zero-based) as the first parameter, and the actual string or other data type to be inserted as the second parameter, as follows:

StringBuffer string1 = new StringBuffer("CBL");
string1.insert(1,"O"); // "COBL"
string1.insert(3,"O");
System.out.println(string1);

This results in the output COBOL. Notice that the insertion point given is the character position before the insertion point.

In addition to append and insert, the setChar and getChar methods change a particular character value in place and retrieve the character value at a specified zero-based position. A method called getChars can return a substring, but in the form of a character array, not a string. This could be converted to a StringBuffer by using the version of append or insert that accepts a character array as input.

There is also an interesting method called reverse that reverses the contents of the string, such that Java would become avaJ. Presumably, there is a use for this somewhere. There must be, because COBOL now has an intrinsic function called REVERSE that does the same thing. We believe it is used to produce the word ECNALUBMA you see at the front of some ambulances so you can read it through your rear-view mirror.

StringBuffer objects support the notion of capacity, that is, of a buffer length greater than or equal to the length of the string literal contained in the StringBuffer. Behind the scenes, the StringBuffer class uses an array of characters to hold the string. The array is given an initial default size, and as the string grows, the array often needs to be reallocated with a bigger size. This behind-the-scenes work is done for you, but there are methods to explicitly set the size (i.e., the capacity) of this buffer. You can thereby optimize performance by predicting the final size you will eventually require, minimizing the need for costly reallocations. With the judicious use of capacity planning, you can benefit from using a StringBuffer object as a "scratch pad" to build up a computed string.

When instantiating an empty StringBuffer, you can specify the initial capacity by passing in an integer value, as follows:

StringBuffer largeString = new StringBuffer(255);

Note that the default capacity for an empty StringBuffer object is 16. Setting the capacity via the constructor is not that useful, however, since you'll usually use another constructor to pass in the initial literal or String object for initializing the buffer. Therefore, aside from setting the initial capacity at instantiation time, you can also use the ensureCapacity method to ensure that the current buffer is at least as large as the number you pass as an argument. If it is not, the buffer size or capacity is grown to the size specified. Despite the method name, ensureCapacity does not return a Boolean value—it does not return anything.

There is also a method called capacity for returning the current capacity. It takes no arguments and returns an integer value. (We would have preferred the method names setCapacity and getCapacity, but alas, we were not consulted.) This notion of capacity and its two methods is also available in other classes in Java that contain "growable" lists, such as the Vector class discussed in chapter 6.

While the ensureCapacity and capacity methods are used to work with a String-Buffer object's buffer size, the setLength and length methods are for working with the actual string's size. This is always less than or equal to the capacity. You can use setLength to grow or shrink the string's size, effectively padding it (with null characters, which are hex zeros) or truncating it. Note that if you set the length of the string to be greater than the capacity, the capacity is automatically grown, just as it is when you grow a string past its capacity using append. On the other hand, if you truncate a string, the capacity is not reduced. To help you see the difference between capacity and length, examine Listing 7.19.

Listing 7.19: The differences between length and capacity in the StringBuffer class.
Start example
public class TestStringBuffer
{
   public static void main(String args[])
   {
      StringBuffer test1 = new StringBuffer(20); // capacity
      test1.append("12345678901234567890"); // string
      System.out.println("String   = \"" + test1 + "\"");
      System.out.println("Capacity = " + test1.capacity());
      System.out.println("Length   = " + test1.length());
      test1.setLength(50); // string length
      System.out.println("———————————————-");
      System.out.println("String   = " + test1 + "\"");
      System.out.println("Capacity = " + test1.capacity());
      System.out.println("Length   = " + test1.length());
      test1.setLength(10); // string length
      System.out.println("———————————————-");
      System.out.println("String   = " + test1 + "\"");
      System.out.println("Capacity = " + test1.capacity());
      System.out.println("Length   = " + test1.length());
   } // end main method
} // end TestStringBuffer class
End example

The result of compiling and running the TestStringBuffer class in Listing 7.19 is the following:

String   = "12345678901234567890"
Capacity = 20
Length   = 20
––––––––––––––––––––––––––––––––
String   = "12345678901234567890
Capacity = 50
Length   = 50
——————————————––––––––––––––––—-
String   = "1234567890"
Capacity = 50
Length   = 10

Notice that the length is always the current number of characters held in the buffer, while the capacity is the maximum number of characters the buffer can hold without it having to be resized (which is done automatically). Notice how calling setLength with a value of 50 extends the actual string itself to be 50 characters long, but uses the null character (all zeros) to pad, so you never see the ending quote. You'd have to subsequently replace all those null character with blanks to get what is probably wanted. Also notice how the capacity always increases when needed, while it never decreases automatically.

Trimming Leading and Trailing Blanks in Java

Now let's go back to the trim operation and see how to implement trim-right and trim-left operations in Java. You saw previously in this chapter how Java's trim method easily trims both leading and trailing blanks. However, if you want to trim only leading or only trailing blanks, you must implement this functionality yourself. With the use of the StringBuffer class, this is not very difficult. The two methods, trimLeading and trimTrailing, are static because you pass in as parameters everything needed. Once again, these useful methods will be placed in the growing OurStringMethods class.

You will be doing a reasonable amount of manipulation on the strings. Thus, both methods start by creating a StringBuffer temporary object from the given String object, and end up using the toString method to convert back. The trimTrailing method is the easiest, as you merely need to find the last non-blank character, and truncate the StringBuffer at that point, using the setLength method, as shown in Listing 7.20.

Listing 7.20: Trimming trailing blanks in Java.
Start example
public static String trimTrailing(String input)
{
   if (input.length() == 0) // error checking
      return input;
   StringBuffer temp = new StringBuffer(input);
   int idx = temp.length()-1;
   // find last non-blank character
   while ( (idx >= 0) &&
         (temp.charAt(idx) == ' ') )
      idx—;
   // truncate string
   if (idx >= 0)
      temp.setLength(idx+1);
   else
      temp.setLength(0);
   return temp.toString();
} // end trimTrailing method
End example

This trimTrailing method looks a little more complicated than it is because you have to carefully deal with the possibility that an input string is all blanks. If so, the method truncates the length to zero. Otherwise, it truncates to one past the last non-blank character.

The trimLeading method is a little more complicated because it involves shifting the characters left, from the first non-blank character. This is best accomplished by brute-force, character-by-character copying. The most efficient way to do this is to use a StringBuffer object that has been initialized to a sufficient capacity, as with the temp2 variable in the trimLeading method in Listing 7.21.

Listing 7.21: Trimming leading blanks in Java.
Start example
 public static String trimLeading(String input)
{
   if (input.length() == 0) // error checking
      return input;
   StringBuffer temp1 = new StringBuffer(input);
   int idx, idx2;
   // find first non-blank character
   for (idx = 0; temp1.charAt(idx) == ' '; idx++);
   // copy characters to new object
   int newSize = temp1.length() - idx;
   StringBuffer temp2 = new StringBuffer(newSize);
   for (idx2 = 0; idx2 < newSize; idx2++, idx++)
      temp2.append(temp1.charAt(idx));
   return temp2.toString();
} // end trimLeading method
End example

Finally, the main method is updated to include simple test cases to drive and demonstrate the new methods, being sure to test the special case when the input string is all blanks:

/*––––––––––––––––––––——————————————————*/
/* Test trimTrailing method             */
/*————————————––––––––––––––––_––––——————/
String paddedString = "  Java For COBOL Programmers  ";
String trimmedRight = OurStringMethods.trimTrailing(paddedString);
System.out.println("\"" + trimmedRight + "\"");
String blankString =
"                              ";
trimmedRight = OurStringMethods.trimTrailing(blankString);
System.out.println("\"" + trimmedRight + "\"");

/*——————————––––––––––––––––––––————————*/
/* Test trimLeading method              */
/*—————————––––––––––––––––––––—————————*/
paddedString =
"  Java For COBOL Programmers  ";
String trimmedLeft = OurStringMethods.trimLeading(paddedString);
System.out.println("\"" + trimmedLeft + "\"");
trimmedLeft =
OurStringMethods.trimTrailing(blankString);
System.out.println("\"" + trimmedLeft + "\"");

The result of compiling and running this is what you would expect:

"  Java For COBOL Programmers"
""
"Java For COBOL Programmers  "
""

These trimTrailing and trimLeading methods should give you a good example of the types of efficient and reusable methods you can write using the StringBuffer class.

Remember that one of the great things about Java is that reusable methods like these are easy to use; just call them from anywhere. There are no copy books to worry about. If the method is static, you just qualify the call with the class name, otherwise, you have to first instantiate the class containing it, and then qualify the method call with the object reference variable. (Of course, you know this well by now!) In this example, the main method calling the new methods is in the same class as the methods, so you did not have to qualify the method calls with the class name. We did this to show you how to utilize these methods from another class, which will usually be the case.


Tokenizing Strings: The Stringtokenizer Class

When writing string-parsing code, you will often want to extract individual words. COBOL has long recognized the need for this, and supplies the UNSTRING statement for it, as shown in Listing 7.22.

Listing 7.22: COBOL's UNSTRING operation.
Start example
WORKING-STORAGE SECTION.
   01 NAME.
      05 NAME-FIRST PIC X(20).
      05 FILLER      PIC X    VALUE SPACE.
      05 NAME-LAST  PIC X(20).
   01 NAME-FULL     PIC X(40) VALUE "Chris Tandy".
   PROCEDURE DIVISION.
   000-MAIN.
      UNSTRING
         NAME-FULL DELIMITED BY SPACE
         INTO NAME-FIRST NAME-LAST.
      DISPLAY NAME.
      STOP RUN.
End example

The example in Listing 7.22 takes a string with the value Chris Tandy and retrieves the first name and the last name, which are delimited by a blank, putting the substrings into the data items NAME-FIRST and NAME-LAST. As the example illustrates, the UNSTRING statement takes the alphanumeric data item to be parsed, the character that delimits the words, and the target data items to place the parsed substrings into. Finally, the DISPLAY statement displays the result of the target data items using the group item containing them. The result is Chris Tandy. Note that the intermediate blank comes from the FILLER item placed between NAME-FIRST and NAME-LAST.

Java also recognizes the need for this function, and although no method exists in the String or StringBuffer classes, Java does supply a utility class in the java.util package named StringTokenizer that does this automatically. This is a good class to know about, as it can save significant coding in those cases where a word-by-word extraction of a given string is required. It is instantiated by specifying the String object to parse. Subsequent iteration through the words, or tokens, is accomplished by the two methods hasMoreTokens and nextToken, as shown in Listing 7.23.

Listing 7.23: Extracting a single word from a string in Java.
Start example
import java.util.*;
/**
* An example of using the StringTokenizer class in the java.util package
*/
public class TestStringTokenizer
{
   public static void main(String args[])
   {
      String inputString = "Mary had a little lamb";
      StringTokenizer tokens = new StringTokenizer(inputString);
      String nextToken;

      System.out.println();
      while (tokens.hasMoreTokens())
      {
         nextToken = tokens.nextToken();
         System.out.println(nextToken);
      }
   } // end main method
} // end TestStringTokenizer class
End example

Running the class in Listing 7.23 results in the following:

Mary
had
a
little
lamb

What delimits or separates words or tokens? By default, it is blank spaces, but this can be explicitly specified at instantiation time by specifying all delimiting characters as a string, for example:

String sample = "  $123,456.78 ";
StringTokenizer words = new StringTokenizer(sample, " $,.");

This specifies four delimiter characters: blank, dollar sign, comma, and dash. You can also specify delimiters as part of the nextToken method call, in the event they are different for each token. The previous example would yield the tokens 123, 456, and 78.

Handy little class, isn't it?


String Class: More Methods

We have yet to discuss a number of remaining methods in the String class that offer additional functionality beyond what COBOL supplies. Rather than describe them all, we leave them to your own discovery. Table 7.2 is a brief summary of some of the more interesting ones.

Table 7.2: A Summary of String Class Methods.
Method | Description
compareTo(String) | Compares two strings lexicographically.
copyValueOf(char[],int,int) | Returns a string that is equivalent to the specified character array.
endsWith(String) | Tests if the string ends with the specified suffix.
equals(Object) | Compares the string to the specified object.
equalsIgnoreCase(String) | Compares the string to another, ignoring case.
getBytes() | Converts the string to a byte array.
getChars(int, int, char[], int) | Copies characters from the substring into the destination character array, starting at the given offset.
hashCode() | Returns a hashcode for the string. Needed by hashtable class.
intern() | Returns a canonical representation for the string, which enables very efficient string comparisons.
regionMatches(boolean, int, String, int, int) | Tests if two string regions are equal.
regionMatches(int, String, int, int) | Tests if two string regions are equal.
startsWith(String) | Tests if the string starts with the specified
prefix. toCharArray() | Converts the string to a new character array.
toLowerCase(Locale) | Converts all of the characters in the string to lower-case using the rules of the given locale.
toUpperCase(Locale) | Converts all of the characters in the string to upper-case using the rules of the given locale.
valueOf(xxx) | Takes as input a primitive data type value and converts it to a string.

Refer to the JDK documentation regarding the java.lang.String class for more detailed information.


Summary

This chapter strings together a number of loose strings. We hope this did not tie you in knots! We touched on the following points:

  • Strings are objects in Java, and instances of the String class.

  • There is built-in language support for defining and concatenating strings. Only strings and arrays enjoy this extra language support. All other classes require the strict use of the new operator and method calls instead of operators.

  • To test for the equality for strings, do not use the equals operator (= =) in Java. Rather, use the equals or equalsIgnoreCase methods supplied as part of the String class.

  • There are many methods in the String class for doing common string manipulations.

  • The String class is immutable, meaning all methods were designed to return new String objects, not affect the current String object.

  • The StringBuffer class is a mutable class, and so can be used to write efficient code for changing or manipulating strings.

  • The StringTokenizer class offers a quick and easy way to parse out words, or tokens, from a given string.


Chapter 8: Date And Time Manipulation

Overview

Chapter 6 mentions that we programmers need arrays ("a raise"). Dare we say that we need a date, too? Maybe, but only if we have time!

Functions that manipulate times and dates are among the most important in any business application. These functions include retrieving the current date or time, and manipulations such as adding a duration to a date, subtracting a duration from a date, extracting parts of a date, and formatting a date or time.


COBOL Date And Time Review

Prior to the enhancements made to COBOL to handle the year 2000, all it had in built-in language support was the ACCEPT DATE statement for getting the current date. This was followed later by some ANSI 85 intrinsic functions and by LE/ILE date/time services, all of which were Y2K ready.

Historically, date and time data items have been coded in COBOL as PIC X(6) or PIC 9(6), with subordinate data items to extract the pieces of the date as required, as in this example:

01 DATE-ORDERED.
   05 ORDERED-YEAR  PIC X(2).
   05 ORDERED-MONTH PIC X(2).
   05 ORDERED-DAY   PIC X(2).
01 TIME-ORDERED.
   05 ORDERED-HOUR   PIC X(2).
   05 ORDERED-MINUTE PIC X(2).
   05 ORDERED-SECOND PIC X(2).

In addition to the Gregorian format for dates shown here, Julian dates have sometimes been used, which have a year part and a three-digit day part.

An alternative to reading the date from a database record is retrieving the current date from the system using the ACCEPT statement. To indicate what part of the date to retrieve, you would specify DATE (a six-digit date), DAY (a five-digit Julian date), TIME (an eight-digit time including hundredths of a second), or DAY-OF-WEEK (one through seven, for Monday through Sunday). The following are examples:

ACCEPT DATE-ORDERED FROM DATE.
ACCEPT TIME-ORDERED FROM TIME.

Of course, all these two-digit years got COBOL programmers into trouble, and now there are the new options DATE YYYYMMDD and DAY YYYYDDD for retrieving four-digit years, as in these examples:

ACCEPT BIG-DATE-ORDERED FROM DATE YYYYMMDD.
ACCEPT BIG-DAY-ORDERED  FROM DAY YYYYDDD.

These require the receiving data items to be defined with a PIC X(4) year subordinate data item. There is also the intrinsic function CURRENT-DATE, which returns a full 21 characters of information. This includes the current Gregorian date in YYYYMMDD format, the time in HHMMSShh format (where hh is hundredths of a second), and +/-HHMM, representing the hours and minutes before or after Greenwich Mean Time. There are also LE and ILE date/time services, which offer Y2K-ready support, but for our purposes of Java comparison, we don't discuss those here.

Note

OS/400 users will have to move to ILE COBOL to get the date and time enhancements discussed here.


COBOL Date And Time Enhancements

COBOL now supports the ANSI 85 intrinsic functions that deal with dates, as shown in Table 8.1.

Table 8.1: Date Data Item Intrinsic Functions.
Intrinsic Function | Description
CURRENT-DATE | Returns a 21-character alphanumeric data item containing current date, time, and offset from Greenwich Mean Time (GMT).
DATE-OF-INTEGER | Converts an integer Gregorian calendar date representing the number of days from December 31, 1600 to a YYYYMMDD format (eight-digit numeric).
DATE-TO-YYYYMMDD | Converts a YYnnnn two-digit integer to YYYYnnnn format, optionally taking a second parameter describing the windowing. For example, 50 means the window begins 50 years prior to when the program is run; 50 is the default.
DATEVAL | An IBM OS/390 extension. When used with the DATEPROC compiler option, treats a given non-date data item as a date data item. A second parameter is a literal describing the date format of the first parameter, such as YYMMDD.
DAY-OF-INTEGER | Converts a Gregorian calendar date to a Julian calendar date (YYYYDDD). The parameter is an integer representing the number of days from December 31, 1600 (seven-digit numeric).
DAY-TO-YYYYDDD | Converts a two-digit-year Julian date to a four-digit-year Julian date (Yynnn to YYYYnnn), optionally taking a second parameter describing the windowing. For example, 50 means the window begins 50 years prior to when the program is run; 50 is the default.
INTEGER-OF-DATE | Takes a YYYYMMDD integer date and returns the number of days it represents from December 31, 1600.
INTEGER-OF-DAY | Takes a YYYYDDD integer date and returns the number of days it represents from December 31, 1600.
UNDATE | An OS/390 COBOL extension. When used with the DATEPROC compiler options, treats the given date data item as a non-date numeric data item.
YEAR-TO-YYYY | Converts a two-digit-year integer to a four-digit year integer, optionally taking a second parameter describing the windowing. For example, 50 means the window begins 50 years prior to when the program is run; 50 is the default.
YEARWINDOW | An OS/390 COBOL extension. When used with the DATEPROC compiler options, returns the starting year of the century window defined in the YEARWINDOW compiler option.

Looking at the intrinsic functions in Table 8.1, you can see that they break down into the following categories:

  • Return a four-digit year date, using the CURRENT-DATE function or the new ACCEPT statement formats.

  • Convert a two-digit year or date to a four-digit year in a specified 100-year window, using the YEAR-YYYY, DAY-TO-YYYYDDD, and DATE-TO-YYYYMMDD functions.

  • Convert a number representing the elapsed days from December 31, 1600 to a date or day format (with a four-digit year) and vice versa, using the DATE-OF-INTEGER and DAY-OF-INTEGER functions, and their reverse equivalents (INTEGER-OF-DATE and INTEGER-OF-DAY).

  • Convert a non-date data item to a date data item and vice versa to enable automatic windowing support, and also retrieve the current century window, using DATEVAL, UNDATE, and YEARWINDOW. (These are only available on OS/390, not OS/400.)

Now let's take a look at the additional enhancements made for date and time support in recent releases of COBOL on OS/390, VM and VSE, and ILE COBOL on OS/400.

OS/390 COBOL Date and Time Enhancements

Starting with version 2, release 1, modification 1 of IBM COBOL for OS/390 and VM, "millennium language extensions" have been added to IBM COBOL to assist with Y2K processing. The extensions are activated using the new DATEPROC and YEARWINDOW compiler options. They require the installation of IBM's VisualAge Millennium Language Extensions for OS/390 and VM (5648-MLE) with your compiler.

These extensions allow you to identify which numeric and alphanumeric data items are in fact date data items, using the new DATE FORMAT clause. You specify this clause together with a format literal that uses the letter Y to identify the digits that represent the year, and X to identify the non-year digits, as in YYXXXX for a typical six-digit, year-month-day data item, as shown here:

05 DATE-ORDERED PIC X(6) DATE FORMAT YYXXXX.

The significance of using this new clause is that it allows you to tell the COBOL compiler (and your fellow programmers) that this is a date data item, and thus to offer special behavior on that data item for certain operations. Specifically, in the PROCEDURE

DIVISION statements, this clause will promote any two-digit years inside identified date data items to four-digit years before executing the statement.

It will decide whether to add 1900 or 2000 to the year depending on the century windowing specified or defaulted in the YEARWINDOW compiler option. This can be a hard-coded year like 1900 (which is added to all date data items), or a sliding number like -50, which is subtracted from the run-date year. The default is 1900, but in either case the final number becomes the base year for the century window. For example, using -50 in the year 2001 gives a base year of 1951, which means all two-digit years between zero and 50 are promoted to 2000 through 2050, while two-digit years between 51 and 99 are demoted to 1951 through 1999. The intrinsic function YEARWINDOW returns this value programmatically, and the intrinsic functions DATE-TO-YYYYMMDD, DAY-TO-YYYYDDD, and YEAR-TO-YYYY allow you to specify the sliding number as the second parameter.

All this automatic promotion is to make existing two-digit-year comparisons work correctly without change, and also to allow you to easily expand a two-digit year to a four-digit year by a simple MOVE or COMPUTE statement, as in the following:

   01 DATE-RETURNED PIC X(6) DATE FORMAT YYXXXX.
   01 DATE-DUE PIC X(6) DATE FORMAT YYXXXX.
   01 DATE-RETURED-4 PIC X(8) DATE FORMAT YYYYXXXX.
   01 DATE-DUE-4 PIC X(8) DATE FORMAT YYYYXXXX.
...
      IF (DATE-RETURNED > DATE-DUE) THEN
         PERFORM OVERDUE-ROUTINE.
      MOVE DATE-RETURNED TO DATE-RETURNED-4.
      MOVE DATE-DUE TO DATE-DUE-4.

This example uses both two-digit-year dates and four-digit-year dates. It does a comparison involving the two-digit-year dates and subsequently expands them to four-digit-year dates. The comparison is done correctly for you, even if the year of the date-returned is 99 and the year of the date due is 00. The moves also result in the correct result, such as 1999 for 99 and 2000 for 00.

There might be times when you don't want these promotions done for you by the compiler. Thus, the UNDATE intrinsic function treats an identified date data item as a normal integer or alphanumeric data item for use in expressions and statements. By "normal," we simply mean no promotion is done by the compiler or runtime. Alternatively, you might choose not to flag date data items with the DATE FORMAT clause, and instead use the DATEVAL intrinsic function to treat a normal non-date data item as a date item on the fly, so promotion will happen in the expression in which it is called.

You might be tempted to think these extensions were simply for temporary help in dealing with the two-digit-year problem in your date data items. However, that is not necessarily the case. We recommend you continue to flag your four-digit years with the DATE FORMAT clause to help you and your fellow programmers easily identify data items that are date data items for documentation purposes. If you do not specify the DATEPROC compiler option, or you specify the NODATEPROC compiler option, this clause is ignored.

These enhancements give some intelligence to COBOL when comparing years and moving year parts of date data items, but it is still up to the programmer to do the proper arithmetic manipulation of the rest of the date. For example, you know that when adding five days to a given numeric date data item, you can't simply add the number 5 to the yyyymmdd data item. You have to consider its effect on the month value, and possibly even the year value. (It is worth noting, though, that math on the year part resulting in a year outside the year window would signal an ON SIZE ERROR, alerting you to the problem.)

We've all written these clever "date rolling" routines. The good news is that Java, as you will see, takes on this burden for you. It gives true and proper date support with built-in date manipulation. It knows, for example, what it means to add two days to February 28.

ILE COBOL on OS/400 Date and Time Enhancements

Starting with version 4, release 2, modification 0 of IBM ILE COBOL for OS/400, much support has been added for date and time support. This has been done not only to aid in the two-digit-year problem, but to offer robust, built-in, true date, time, and timestamp support.

You now identify your date, time, and timestamp data items using the FORMAT clause on the data item declaration. The FORMAT clause is used instead of the PIC clause, and is followed by DATE for date data items, TIME for time data items, or TIMESTAMP for date and time data items. Further, DATE and TIME data items can specify a format literal, identifying the format of the date or time. These are quoted literals containing special characters like @Y, called conversion specifiers, together with a separator character, as in these examples:

01 DATE-ORDERED FORMAT DATE "@Y/%m/%d".
01 TIME-ORDERED FORMAT TIME "%H:%M:%S".

These format literals tell COBOL the size and format of the data item. There is a specific list of conversion specifiers that ILE COBOL defines. Each has a predetermined length and meaning (for example, @Y means a four-digit year), and the separator characters you optionally include between the conversion specifiers all take up one character and add one to the length.

The first example here defines a date data item that holds dates of the form YYYY/MM/DD, and so is ten characters long. The second example is a time data item that holds times of the form HH:MM:SS, so it is eight characters long. There is no USAGE clause, so the default is DISPLAY. You may specify USAGE PACKED-DECIMAL, but in this case only numeric conversion specifiers are permitted, and no separators can be specified in the format literal.

The conversion specifiers allowed in date data-item format literals are shown in Table 8.2.

Table 8.2: ILE COBOL Conversion Specifiers for Date Data Items.
Conversion Specifier | Description
@C | Replaced by a one-digit number, 0 through 9, representing the century, where 0=1900, 1=2000, etc.
%d | The day of the month as a two-digit integer from 01 to 31.
%D | The same as %m/%d/%y (and hence eight bytes long).
%j | The day of the year as a three-digit integer from 001 to 366.
%m | The month as a two-digit integer from 01 to 12.
%y | A two-digit year.
@Y | A four-digit year.
%% | A simple % literal separator character.
@@ | A simple @ literal separator character.

The conversion specifiers allowed in time data-item format literals are shown in Table 8.3.

Table 8.3: ILE COBOL Conversion Specifiers for Time Data Items.
Conversion Specifier | Description
%H | The hour as a two-digit integer from 00 to 23.
%I | The hour as a two-digit integer from 01 to 12.
%M | The minute as a two-digit integer from 00 to 59.
@p | A two-character AM or PM, depending on the hour.
%R | The five-digit, 24-hour time in the format HH:MM.
%S | The second as a two-digit integer from 00 to 59.
@Sh | The hundredth of a second as a two-digit integer.
@Sm | The millionth of a second as a six-digit integer.
@St | The tenth of a second as a one-digit integer.
%% | A simple % literal separator character.
@@ | A simple @ literal separator character.
@So | The thousandth of a second as a three-digit integer.

As you can see, these conversion specifiers are case-sensitive. Note that TIMESTAMP data items do not allow a format literal. These are predefined to be date and time data items of

length 26 and format @Y-%m-%d-%H.%M.%S.@Sm.

You can omit the format literal from the FORMAT clause for DATE and TIME data items too, in which case you get the ISO standard of @Y-%m-%d (YYYY-MM-DD) for dates, and %H.%M.%S (HH.MM.SS) for times. Alternatively, you can explicitly specify default format literals for date and time data items that have no format literals, in the SPECIAL-NAMES section:

SPECIAL-NAMES.   FORMAT OF DATE IS "@Y/%m/%d",
                 FORMAT OF TIME IS "%H:%M:%S".

Note that these notions of date, time, and timestamp data items are not unique to COBOL. They have existed in DB2/400 as data types for many years, and hail from the days of SAA. Now, finally, we can write COBOL programs to read them in, work with them, and write them out again properly, instead of converting them internally to non-date types.

To make this happen, you must specify the *DATE, *TIME, and *TIMESTAMP parameter values on the compiler's CVTOPT option, as well as the *DATETIME parameter value. Of course, even if you are not using these data types in your database, you can still use the FORMAT clause in your COBOL program, but in this case, you must also specify the USAGE PACKED-DECIMAL clause and specify the *CVTTODATE value of the CVTOPT compiler option.

Another thing to note is that DB2/400 allows you to specify date and time formats in your fields, using the DATFMT and TIMFMT keywords. These allow you to choose from a specific list of predefined formats, such as *ISO for YYYYMMDD or *LONGJUL for YYYYDDD. By specifying the *DATE and *TIME parameter values mentioned previously, these will come into your COBOL program with the appropriate FORMAT clause and literal format already defined to match the DDS format. Chapter 15 of the ILE COBOL for AS/400 Programmer's Guide contains a table showing the mapping of these DDS formats to COBOL formats.

So now that your date data items are all nicely identified in your COBOL program by way of the FORMAT clause, what is the point? The point is two-fold. First, COBOL can perform automatic century windowing for you until you get all date data items converted to four-digit years. Second, you can use some new intrinsic functions that make date arithmetic much easier. For some nice new functionality that will save you lots of coding, look into the new intrinsic functions listed in Table 8.1 and Table 8.4.

Table 8.4: ILE COBOL Intrinsic Functions for Date-Time Manipulations.
Intrinsic Function | Description
ADD-DURATION | Adds a duration to a given date, time, or timestamp data item, and returns the new date, time, or timestamp. The first parameter is the data item to add to, the second parameter is the type of duration to add, and the third parameter is the numeric duration to add. The second parameter is either YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, or MICROSECONDS. Parameters 2 and 3 can be repeated to add multiple durations of differing types. This function handles rolling the appropriate date parts so a valid resulting date is returned. SUBTRACT-DURATION does the reverse.
CONVERT-DATE-TIME | Converts a non-date data item or literal to a date, time, or timestamp data item. Parameter 1 is the data item or literal to be converted, parameter 2 is one of DATE, TIME, or TIMESTAMP indicating what to convert to, and parameter 3 is the format literal for the returned date, time, or timestamp data item. Parameter 3 can be a literal or the special FORMAT OF data-item register that returns the format literal of the specified date or time data item.
EXTRACT-DATE-TIME | Extracts part of a date, time, or timestamp data item. The item to extract from is given in parameter 1, while parameter 2 identifies the part to extract, using one of the keywords YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, or MICROSECONDS.
FIND-DURATION | Returns the duration between the two given dates, times, or timestamps given in the first two parameters. The third parameter specifies the units of duration to return: YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, or MICROSECONDS. Returns a positive or negative nine-digit integer.
SUBTRACT-DURATION | Same as ADD-DURATION, but for subtracting instead of adding.
TEST-DATE-TIME | Takes a date, time, timestamp, alphanumeric, packed, or zoned data item and tests if it is a valid date, time, or timestamp. Parameter 1 is the data item, parameter 2 identifies the test type (DATE, TIME or TIMESTAMP), and parameter 3 is the format literal (either a literal or FORMAT OF data-item). Returns Boolean B"0" if parameter 1 is not valid, or B"1" if it is.

For automatic windowing, simply specify the FORMAT clause (or generate from COPY DDS), and all comparisons of dates with two-digit years will work correctly, without changes. This is because ILE COBOL will automatically promote all two-digit years to four-digit years internally, using the century window in effect. The default century window is 40, meaning that any two-digit year less than 40 will become a century-2000 year, while any two-digit year greater than or equal to 40 will become a century-1900 year. To override this default, use the DATTIM process option, as in DATTIM(1900, 20), which changes the century window to 20, or 1920 through 2019.

We think you will agree that the functions in Table 8.4 can save you a lot of otherwise tedious programming. Yes, by now you have probably got your own library of such routines, but if you do not, or if you want to get out of the business of maintaining it, we very strongly recommend looking at these intrinsic functions. As you will see shortly, Java supplies all this functionality and more for date and time processing.

Finally

We assume you are using four-digit years now, or at least century windowing for short-term solutions, whether you are on OS/390, OS/400, or both. Now that we have reviewed the "state of the union" in terms of COBOL support for date and time, let's look at what Java provides for built-in date and time support.


Java Date And Time Support

Java does not offer primitive types for dates and times, as you might notice in the discussion of primitive types in chapter 5. Instead, it offers classes to represent dates and times. These classes are rich with methods that retrieve, compare, add, subtract, extract, format, and test both dates and times. Again, you will see here the power and elegance of classes and objects in their ability to encapsulate in one place all the algorithms and functions needed to fully support a particular concept, as well as the ability to easily re-use that well-tested code, forever, in all applications. It happens that the Java engineers designed and wrote these classes for you, but you should be thinking of the other classes you could create to encapsulate other code you have found yourself rewriting time and again.

Java's support for dates and times can be broken down into the areas shown in Table 8.5, which also shows the classes supplied to address those areas. Note that Java does not separate date and time, but rather encapsulates them into a single object that contains both, and gives you the ability to set, extract, and manipulate just the parts you happen to be interested in.

Table 8.5: Java Support for Date and Time.
Date/Time Functionality | Java-Supplied Classes
The actual date and time, plus operations common to both date and time (setting,getting, comparing, adding, or subtracting durations). | The Date class in the java.util package, al-though this is an old class not used as much anymore. The GregorianCalendar class in the java.util package. Method currentTimeMillis in the System class of the java.lang package.
Formatting the date and time for purposes of displaying. | The SimpleDateFormat class in the java.text package.
Getting views of the same date and time in different time zones. | The TimeZone class in the java.util package.

The remainder of this chapter shows you some of the capabilities of these Java classes, which hint at the rich functionality they support.

Raw Time

Every computer contains a built-in clock as part of its hardware, which (surprise) keeps track of time. This clock is used by the CPU (Central Processing Unit) for hardware purposes, and by the operating system to establish the current date and time when you turn on your machine. If you have ever wondered how the clock stays accurate while your machine's main power supply is off, it typically uses a battery. The clock, unlike the one you have hanging on the wall, keeps track of time in milliseconds. Such precision is necessary for computers, of course, while your kitchen clock can easily do without it (depending on the precision of your recipes).

If you write a little program to read the clock from your hardware, you will end up with numbers such as 6874732864982. How do you interpret these numbers? They are the total number of milliseconds that have elapsed since a universally recognized time and date in history, called the epoch. For COBOL, the base year, or epoch, is January 1, 1940. For Java, it is January 1, 1970.

But, back to the computer clock. What does the number returned mean to you and to your application? It really means that the clock inside your computer is continually incrementing its count of milliseconds. When your program asks for that value, the clock returns the number of milliseconds it has counted, in the case of Java, since January 1, 1970.

This base line is needed to provide a standard base from which computers or programming languages do their date and time manipulation. For example, the currentTime Millis static method in the java.lang.System class returns the number of milliseconds from the computer clock.

Take a look at Listing 8.1. This example calls a method currentTimeMillis in the Java-supplied class System to retrieve the number of milliseconds elapsed from the base date. Remember that the package java.lang that contains the System class is always imported for you. This method returns a long primitive value containing the number of milliseconds.

Listing 8.1: Retrieving elapsed time using method currentTimeMillis.
Start example
public class MyTimeMilliseconds
{
   public static void main(String args[])
   {
      long timeInMil;
      timeInMil = System.currentTimeMillis();
      System.out.println("Time is: " + timeInMil);
   }
}
End example

Is this useful? Well, sometimes it is, such as if you want to calculate the elapsed time for a particular operation. You could simply record this value at the beginning of the operation, and again at the end of the operation, and then take the difference to determine how long it took. You could divide the difference by 1,000 to get the number of seconds, if you wanted.

Here's an opportunity for your own little utility class, already! Assuming you might need to record elapsed time during the performance-tuning part of development, why don't you design a little class that will do this? It needs two instance variables to record the start and end times, and two methods to set them, as well as a method to subsequently return the elapsed time in terms of milliseconds. It would also be nice to return the elapsed time in the form of a string that shows "hh hours, mm minutes, ss seconds, nn milliseconds." This class is given in Listing 8.2. We decided to call it ElapsedTime.

Listing 8.2: The ElapsedTime class.
Start example
public class ElapsedTime
{
   private long startTime;
   private long endTime;

   public void setStartTime()
   {
      startTime = System.currentTimeMillis();
   }
   public void setEndTime()
   {
      endTime = System.currentTimeMillis();
   }
      public long getElapsedTime()
   {
      return (endTime - startTime);
   }
   public String toString()
   {
      long deltaMillis = getElapsedTime();
      // deltaMillis = n + s*1000 + m*60*1000 + h*60*60*1000;
      long millis       = (deltaMillis) % 1000;
      long deltaSeconds = (deltaMillis) / 1000;
      long deltaMinutes = deltaSeconds  / 60;
      long deltaHours   = deltaSeconds  / (60 * 60);
      long hours  = (int)(deltaMillis   / (60 * 60 * 1000));
      long minutes= (int)(deltaMinutes - (hours*60) );
      long seconds= (int)(deltaSeconds - (hours*60*60) - (minutes*60));
      String result = "Elapsed time: " + hours   + " hours, " +
                                        minutes + " minutes, " +
                                        seconds + " seconds, " +
                                        millis  + " milliseconds";
      return result;
   }
} // end class ElapsedTime
End example

We won't bother dissecting this little class, but leave it to you. The interesting part, of course, is the algorithm in the toString method for converting a raw number in milliseconds to the accurate number of hours, minutes, seconds, and milliseconds it represents. The cool thing is, once you have figured out the algorithm and tested it "nine ways to Sunday" (a date expression), you can confidently flush the details from your mind, and reuse it forever.

To use this class to record the time to do any operation, simply instantiate an object of it, call setStartTime on the object before the operation starts and setEndTime after it ends, and then System.out.println. Because the method is called toString, it will be called automatically when an instance of this class is specified in the System.out.println method call. Note that you can time multiple operations simultaneously, simply by instantiating as many instances of the class as you need. (That's the beauty of objects.) One enhancement you might want to make is to define a default constructor that calls setStartTime so that users don't have to explicitly call it if they instantiate an object at the right time.

If You Have the Time, I Need a Date

Getting the current time in terms of milliseconds from the epoch is nice, but certainly not the usual thing to do. Rather, the most pressing requirement is to get the current date and time in a form that is meaningful. By "meaningful," we mean in a form that allows you to extract or display it in terms of the year, month, day, hour, minute, second, or some subset of these. This is what COBOL does with the ACCEPT statement or the CURRENT-DATE intrinsic function, as shown here:

01 START-RENTAL.
   05 RENTAL-DATE FORMAT DATE "@Y%m%d".
   05 RENTAL-TIME FORMAT TIME "%H:%M:%D".
* . . .
  ACCEPT RENTAL-DATE FROM DATE YYYYMMDD.
  ACCEPT RENTAL-TIME FROM TIME.

In Java, you can do this by simply instantiating the Date class from the java.util package, as shown in Listing 8.3.

Listing 8.3: The Date class from java.util.
Start example
import java.util.*;

public class GetDate
{
   public static void main(String args[])
   {
      Date rentalDate = new Date();
      System.out.println(rentalDate);
   }
}
End example

Compiling and running this on the day this was written, on a computer set for the Eastern Standard Time time zone, results in the following:

Thu Jun 03 21:28:27 EDT 1999

The Date class is the basic class in Java for representing a date (and a time, actually) in terms of years, months, days, hours, minutes, seconds, and milliseconds. It is the original class Java supplied for this function, but since JDK 1.0, it has been deprecated (meaning it is no longer preferred) in favor of the GregorianCalendar class, also in the java.util package.

GregorianCalendar really deals with, and specializes in, manipulating dates—like adding durations to dates, as you will see. Internally, it still uses a Date object to hold the actual date and supplies a method called getTime (strangely) to retrieve it. This is necessary because the Date object is still the preferred object to use for displaying dates such as with System.out.println, since it formats them nicely. Thus, the preferred way to get the current date is to instantiate GregorianCalendar, and the easiest way to display that date is to use getTime to display the resulting Date object, as shown in Listing 8.4. This results in the same output as Listing 8.3.

Listing 8.4: Using getTime to display a Date object.
Start example
import java.util.*;

public class GetDate
{
   public static void main(String args[])
   {
      System.out.println();
      // Date rentalDate = new Date();
      // System.out.println(rentalDate);
      GregorianCalendar rentalDate = new GregorianCalendar();
      System.out.println(rentalDate.getTime());
   }
}
End example

You can also use other constructors in Date and GregorianCalendar to create specific date and times instead of the current date. For example, Date has a constructor that takes a long value representing a number of milliseconds from the epoch, such as System's currentTimeMillis method returns, creating an object for the date and time that the millisecond value represents. (The Date class also has a method getTime that returns the milliseconds from the epoch value for the date the object represents.)

In addition, the GregorianCalendar class has constructors for specifying (as integer values) the year, month, and day, or the year, month, day, hour, minute, and second, as shown in Listing 8.5. Note that the month, and only the month, is specified as zero-based.

Listing 8.5: Examples of value options for the GregorianCalendar class.
Start example
GregorianCalendar xmas01     =
   new GregorianCalendar(2001,11,25);
GregorianCalendar xmas01noon =
new GregorianCalendar(2001,11,25,12,0,0);
System.out.println(xmas01.getTime());
System.out.println(xmas01noon.getTime());
End example

Here is the result of running Listing 8.5:

Tue Dec 25 00:00:00 EST 2001
Tue Dec 25 12:00:00 EST 2001

Note that there are also constants defined in GregorianCalendar for the months, so you do not have to remember to zero-base them. The constants are the actual English names for the months, all in uppercase, for example, GregorianCalendar.DECEMBER. The JDK documentation does not list these constants because they come from the parent class Calendar. Look up the Calendar class to see all the handy constants defined there, such as the days of the week. (The concept of parent and child classes is covered in chapter 9.)

While GregorianCalendar does not have a constructor that takes a Date object, it does have a setTime method that takes one and sets the internal date from it. There might be times when you have a Date object and wish to make it a GregorianCalendar object. This can be done by using this method after instantiation, as in the following:

Date dateObject = new Date();
GregorianCalendar gcObject = new GregorianCalendar();
gcObject.setTime(dateObject);

One last and important thing to note about dates is how to store them in a database. In chapter 13, you see that you can retrieve database date, time, and timestamp fields (that is, data items defined with the SQL or OS/400 DDS field types for these) directly into objects of class Date, Time, and Timestamp. These classes are all in the java.sql package, and they are all child classes of the Date class (in java.util). Therefore, they can be used directly as though they were java.util.Date objects. This means you can turn them into GregorianCalendar objects, for example, by calling the setTime method on a GregorianCalendar object, as just discussed.

To go the other way, and create these objects from your GregorianCalendar objects (for the purposes of writing to the database), one option is to use the constructor version of these objects, which takes a long value representing the milliseconds from the epoch. You can get this value from a GregorianCalendar object by calling its getTimeInMillis method, as in the following:

GregorianCalendar gc = new GregorianCalendar();
java.sql.Date databaseDate = new
java.sql.Date(gc.getTimeInMillis());

Enough time on how to get a date, now let's compare dates.

Comparing Dates

In COBOL, to compare two dates, you can simply use comparative statements like IF to find out if a date is before, after, or equal to another date value. For four-digit-year dates in year-month-day sequence, this is simply a regular numeric or alphanumeric comparison. For two-digit years, you need to rely on the century window support built into COBOL, as described at the beginning of this chapter.

How are date comparisons done in Java? Well, dates are objects in Java, not primitive types, so using the == operator will only compare memory addresses, and the > and < operators are not allowed at all. Instead, you need to rely on methods that are, it is hoped, supplied in the class. In fact, Java has such methods in its GregorianCalendar class, namely the after, before, and equals methods. These methods all take a second GregorianCalendar object as the only parameter and return true if that date is after, before, or equal to this object's date, respectively.

Listing 8.6 illustrates using the before method to determine if a date is before another date. This example creates two GregorianCalendar objects, gc1 and gc2, representing Dec. 31, 2000, and Jan. 1, 2001, respectively. It then uses the before method to determine if the gc1 date is before gc2. As a result, the program prints "Yes it is," as you would expect. If you substitute the after or equals methods, the result would be "No it is not."

Listing 8.6: An example of the before method.
Start example
import java.util.*;

public class CompareDates
{
   public static void main (String args[])
   {
      GregorianCalendar gc1 = new GregorianCalendar(2000, 11, 31);
      GregorianCalendar gc2 = new GregorianCalendar(2001, 0, 1);
      if (gc1.before(gc2))
         System.out.println("Yes it is");
      else
         System.out.println("No it is not");
   }
} // end CompareDates
End example


Date And Time Manipulation

Let's look now at how to do date math and date-part extraction.

Date/Time Math and Duration

You have probably written code to add days, months, or years to a given date, or hours, minutes, or seconds to a given time. You know that you have to take special care to potentially "roll" the month and possibly the year when adding days, or to roll the minute and possibly the hour when adding seconds. For example, adding one day to December 31 changes not only the day, but the month and the year. Similar considerations apply to subtracting a duration from a given date.

Another common requirement is to subtract one date from another and determine the elapsed time in some units, such as days. For example, you might have to calculate the elapsed days between a rental date and a returned date, in order to compute the total cost of the transaction. Historically, COBOL did not distinguish between date data items and regular numeric data items, and hence did not offer any help with these calculations.

OS/400 ILE COBOL programmers now have the benefit of the intrinsic functions ADD-DURATION, SUBTRACT-DURATION, and FIND-DURATION, as described earlier in Table 8.4. An example of their use is shown in Listing 8.7. (For the benefit of OS/390 readers, we don't dissect this example.)

Listing 8.7: Examples of COBOL duration functions.
Start example
WORKING-STORAGE SECTION.
01 RENTAL-TYPE PIC X(1) VALUE "S".
   88 RENT-START VALUE "S".
   88 RENT-END   VALUE "E".
01 START-RENTAL.
   05 RENTAL-DATE FORMAT DATE "@Y%m%d".
   05 DUE-DATE    FORMAT DATE "@Y%m%d".
01 END-RENTAL.
   05 RETURN-DATE FORMAT DATE "@Y%m%d".
   05 DAYS-RENTED PIC 9999.

PROCEDURE DIVISION.
000-MAIN.
* * *
   IF (RENT-START) THEN
      ACCEPT RENTAL-DATE FROM DATE YYYYMMDD
      MOVE FUNCTION ADD-DURATION (RENTAL-DATE, DAYS, 2)
         TO DUE-DATE
   ELSE
      ACCEPT DUE-DATE FROM DATE YYYYMMDD
      COMPUTE DAYS-RENTED =
         FUNCTION FIND-DURATION (DUE-DATE, RENTAL-DATE, DAYS)
      DISPLAY DAYS-RENTED
   END-IF
End example

Java, like ILE COBOL on OS/400, has built-in functionality for calculating durations on dates. This is the primary purpose of the GregorianCalendar class versus its simpler cousin, the Date class. While Date objects just store a date and allow simple operations on it, such as displaying them, GregorianCalendar objects allow for complicated operations, such as date comparisons and duration computation. To do this, of course, complicated algorithms must be built into the class, along with a deep knowledge of the Gregorian calendar. (There is no calendar other than Gregorian supported in Java today, but the architecture is there to easily allow others to be written. They are expected in the future, and might be there by the time you read this.)

The GregorianCalendar class has the method add, which allows you to add a date part, such as days, to a date. The internal date value will change appropriately, as shown in Listing 8.8.

Listing 8.8: Adding date parts to a date in Java.
Start example
import java.util.*;

public class AddDateDurations
{
   public static void main(String args[])
   {
      GregorianCalendar gc
      = new GregorianCalendar(2000,Calendar.DECEMBER,31);
      System.out.println("The date before addition: " + gc.getTime());
      gc.add(GregorianCalendar.DATE,2);
      System.out.println("The date after addition : " + gc.getTime());
      gc.add(GregorianCalendar.YEAR,2);

      System.out.println("The date after addition : " + gc.getTime());
   }
} // end AddDateDurations
End example

Listing 8.8 first instantiates the GregorianCalendar class with parameters to give a date of December 31, 2000. After printing the current date in the calendar, it uses the add method to add two days to the current day, and then add two years to that result. The result of the first add is "Jan.2,2001," and the result of adding two to the year is "Jan.2,2003." Note that the second addition retains the day value from the first addition:

The date before addition: Sun Dec 31 00:00:00 EST 2000
The date after addition : Tue Jan 02 00:00:00 EST 2001
The date after addition : Thu Jan 02 00:00:00 EST 2003

What tells the add method which part of the date you are adding, for example, day versus year? As you can see from the example, this is the purpose of the first parameter used in the call. Java uses the constants YEAR, MONTH, DATE, HOUR, MINUTE, SECOND, and MILLISECOND to distinguish the type of addition.

To prove to yourself that the "rolling" behavior of the add method is correct, add one day to February 28, both on a leap-year date and not on a leap-year date. You will see that it works correctly. The GregorianCalendar class provides the standard calendar used by most of the world, but its parent, the Calendar class, is designed to allow others to easily snap in their own. If you look up the variable index in the JDK for the Calendar class, you will see a lot of constants that you can reference when doing date manipulation.

So how do you subtract durations from a date? The answer is the add method again! Remember that adding a negative number is equivalent to subtracting. For example, adding the following two lines of code to the previous example will result in subtracting four years from the current date (be careful—you might go back in time!):

gc.add(Calendar.YEAR,-4);
System.out.println("The date after deletion:  " + gc.getTime());

How do you get the number of years, days, or months between two given dates? Unfortunately, there is no method in Java to subtract one date from another and express the result in a particular unit of measurement. You are on your own for this, and it is really a shame that Java doesn't support it. However, it is an easy matter to write the code: just call currentTimeInMillis on both GregorianCalendar objects, and subtract the larger from the smaller (or use the abs method in the Math class). Then, divide the difference by 1000*60*60*24 to get the number of elapsed days, for example. If you want to print the difference in an attractive way, use the ElapsedTime class from Listing 8.1.

Extracting Parts of a Date or Time

Often, you'll need to display date information in a meaningful way. For example, you might want to print the day of the week as a string, such as "MONDAY." To do this in COBOL, you just declare a table of the seven weekday names, and then extract from the date the day expressed as a number from one to seven, which you use to index into the table, as shown in Listing 8.9.

Of course, you don't usually have the day expressed as a number from one to seven, but as the day in the month, from one to 31. The only trick then is performing a conversion, which is easily done by subtracting from the date another, hard-coded date that is known to be a Sunday in the past. Of course, you have to normalize the dates to days first, before doing the subtraction. The intrinsic functions INTEGER-OF-DAY and INTEGER-OF-DATE do this for you. This gives a duration in days, from which you take the remainder after dividing by seven, and add one. You can do a similar thing for months, but they are much easier because they are already in the form needed for a table index.

Listing 8.9: Displaying date information in COBOL.
Start example
WORKING-STORAGE SECTION.
01 WEEKDAYS VALUE
   "MONDAY   TUESDAY   WEDNESDAYTHURSDAY FRIDAY   SATURDAY SUN
-   "DAY".
   05 DAYNAME PIC X(9) OCCURS 7 TIMES.
01 CURRDAY PIC 9.

PROCEDURE DIVISION.
000-MAIN.
* * *
   ACCEPT CURRDAY FROM DAY-OF-WEEK.
   DISPLAY DAYNAME (CURRDAY).
End example

In Java, you could do exactly the same thing by simply using the get method in the GregorianCalendar class, which extracts a particular part of the date. This is identified using constants in the GregorianCalendar class, as shown in Listing 8.10.

Listing 8.10: Displaying date information in Java.
Start example
import java.util.*;

public class GetDayOfWeek
{
   public static final String days[] = {
      "SUNDAY","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY",
      "SATURDAY"};

   public static void main(String args[])
   {
      GregorianCalendar today = new GregorianCalendar();
      int dayOfWeek = today.get(GregorianCalendar.DAY_OF_WEEK);
      String dayAsString = days[dayOfWeek-1];
      System.out.println(dayAsString);
   }
}
End example

As in COBOL, you declare an array and initialize it to the names of the days. Note that the array starts at Sunday, not Monday. You then create the GregorianCalendar object, in this case to retrieve today's date. Once you have that, you can call the get method to find the day of the week, which it returns as a number from one to seven, where Sunday is one. You simply use that number, minus one, to index into the array. The println method prints the name of the day as a string. For example, running this on a Friday would produces the string "FRIDAY," as expected. The other constants you can specify on the get method for dates are shown in Table 8.6.

Table 8.6: Constants for Extracting Date Parts via the get Method in the GregorianCalendar Class.
Constant | Description
DATE or DAY_OF_MONTH | The one-based day in the month (1 to 31).
DAY_OF_WEEK | The one-based day in the week (1 to 7).
DAY_OF_WEEK_IN_MONTH | The ordinal day of the week within a month (1 to 5). For example, it returns 2 if this is the second Friday in the month.
DAY_OF_YEAR | The day within the year (1 to 365).
ERA | The era as a constant: AD or BC.
YEAR | The year (four digits, of course).
WEEK_OF_YEAR | The week number within the year (1 to 52).

Of course, GregorianCalendar's objects also capture the time, not just the date, so there are constants for extracting time parts as well, as shown in Table 8.7. In addition to this very handy get method, there is also a set method, which takes the same constants and a second parameter, which is the value to set that date or time part to.

Table 8.7: Constants for Extracting Time Parts via the get Method in the GregorianCalendar Class.
Time Constant | Description
AM_PM | A constant, AM or PM, for morning or afternoon.
HOUR | The 12-hour number (1 to 12).
HOUR_OF_DAY | The 24-hour number (1 to 24).
MINUTE | The minute within the hour (0 to 59).
SECOND | The second within the minute (0 to 59).
MILLISECOND | The millisecond within the second (0 to 999).
DST_OFFSET and ZONE_OFFSET | The offset in milliseconds from Daylight Savings Time and Greenwich Mean Time.


Formatting Date And Time

The following sections describe how to format dates and, after that, times. You will find that for the specific problem of printing all or part of a date or time in a nicely formatted way, there is tremendous built-in support in Java.

Formatting Date Data Items

In COBOL, you format dates the way you format everything, using the PICTURE clause, or using a group item with FILLER items for the separators. In addition, OS/400 COBOL programmers now have the ability to specify formatting information using the format literal with the FORMAT clause, as describing at the beginning of this chapter.

In Java, the JDK supplies a SimpleDateFormat class in the java.text package explicitly for this purpose. It allows you to choose any user-defined pattern for formatting, as in Listing 8.11.

Listing 8.11: Formatting date data items in Java.
Start example
import java.text.*;
import java.util.*;

public class TestDateFormat
{
   public static void main (String args[])
   {
      Date date = new Date();
      SimpleDateFormat test = new SimpleDateFormat("yyyy/MM/dd");
      String dateString = test.format(date);
      System.out.println("After formatting:  " + dateString);
   }
}
End example

Listing 8.11 first instantiates a Date object with today's date. It uses Date instead of GregorianCalendar because that is what SimpleDateFormat requires, although you would usually get a Date object by simply calling the getTime method on a Gregorian-Calendar object. It then instantiates a SimpleDateFormat object and passes the pattern to use for date formatting to it as a parameter. This is now a date-formatting machine! You can simply call its format method as many times as needed, with any Date object, and that date will be formatted according to the originally supplied pattern. Doing this with our current Date object results in the following:

After formatting:  2000/03/04

The only thing you need to change to establish a new format is the value of the pattern, either by instantiating a new object or using the applyPattern method. The SimpleDate-Format class allows you to specify various (case-sensitive) characters as part of the string to indicate different formatting patterns. These are predetermined for specific date parts, as shown in Table 8.8.

Table 8.8: Date Formatting Symbols for the SimpleDateFormat Class.
Character | Meaning
G | The era designator—text.
y | The year—numeric.
M | The month in the year—text and number.
d | The day in the month—numeric.
E | The day in the week—text.
D | The day in the year—numeric.
F | The day of the week in the month—numeric.
w | The week in the year—numeric.
W | The week in the month—numeric.
' | Escape for text—delimiter.
'' | Single quotes around literals.

As you see, Listing 8.11 uses the y, M, and d characters for the pattern. Notice that the uppercase M is used to show the month in the year, and the low- ercase d and y to show the day in the month and the year, respectively. Listing 8.12 shows a few more examples of date formatting, which give the following results:

Formatted date: 06.155.00 AD JAVA4CBL
Formatted date: Day of week: Friday

Notice the use of G in the format to display the era, and the use of a literal 'JAVA4CBL'. Pay particular attention to the use of single quotes around the literal. When you specify string literals in the format, you must enclose them in single quotes; otherwise, Java assumes that each letter is meant to be one of the substitution variables. The exception to this rule is the separator characters: slash, dash, period, ampersand, and comma. These characters need not be enclosed in quotes.

Listing 8.12: Date formatting examples, using both symbols and literals in the format.
Start example
Formatted date: 06.155.00 AD JAVA4CBL
Formatted date: Day of week: Friday
import java.text.*;
import java.util.*;

public class TestDateFormat2
{
   public static void main (String args[])
   {
      Date date = new Date();
      SimpleDateFormat testA = new SimpleDateFormat("MM.DD.yyy G 'JAVA4CBL'");
      SimpleDateFormat testB = new SimpleDateFormat("'Day of week:' EEEE");
      String dateStringA = testA.format(date);
      String dateStringB = testB.format(date);
      System.out.println("Formatted date: " + dateStringA);
      System.out.println("Formatted date: " + dateStringB);
   }
}
End example

The second pattern uses the E character to display the day of week. Earlier in this chapter, you saw a technique to display the name, in text, for the day of the week, by using the get method in the GregorianCalendar class, and indexing into an array that lists the names for all seven days. As it turns out, the SimpleDateFormat class can do the same thing, using the E pattern as shown in the example.

You might be wondering why you would want to use multiple characters in the patterns, such as yyyy, MM, and dd. One reason is that it helps fellow programmers easily see at a glance precisely the pattern, including lengths for the constituent date parts, you hope to achieve. Of course, it is more than this. The repetition of the symbols has precise meaning to the SimpleDateFormat class—well, sort of precise.

Notice in Table 8.8 that some of the symbols expand into numbers, such as 1999, and some expand into text, such as Friday. For the number symbols, the count of repeated symbols in the format string tell Java exactly how many digits to show. If the count is greater than the resulting number, the number is zero-padded on the left. For example, the month of June is displayed as 06 because two Ms were used in the pattern. If the count is too small to hold the result, it will not be truncated. Thus, using one M for the month will still show 12 for December, but just 6 for June. The year is an exception, since it will truncate to two digits (but no less) if only two digits are given.

For symbols that produce text, like E for the day in week, there are usually two forms for the output, a short form such as Fri and a long, or full, form such as Friday. Java uses the long form only if the symbol is repeated four or more times. For symbols that can be either text or a number, such as M for month, Java uses text (such as January) if the symbol is repeated three or more times, and the number (such as 01) otherwise.

Formatting Time Data Items

In addition to formatting a Date object for date display, you can also, of course, format it for time display, since a Date object incorporates both date and time values, as Listing 8.13 shows.

Listing 8.13: Formatting time data items.
Start example
import java.text.*;
import java.util.*;

public class TestTimeFormat
{
   public static void main(String args[])
   {
      Date date = new Date();
      SimpleDateFormat test = new SimpleDateFormat("hh:mm:ss");
      String timeString = test.format(date);
      System.out.println("After formatting: " + timeString);
   }
}
End example

This is identical to the previous date-formatting example, except it specifies "hh:mm:ss" for the format instead of "yyyy/MM/dd". Because only letters pertaining to time are specified, the result shows only a formatted time. Thus, if the current time is 10:25:30, after executing the program, the timeString object will contain the value 10:25:30. The letters that you can specify in the format pattern pertaining to time are shown in Table 8.9.

Table 8.9: Time-Formatting Symbols for the SimpleDateFormat Class.
Character | Meaning
h | The hour in AM/PM format—a number from 1 to 12.
H | The hour within the day—a number from 0 to 23.
m | The minute within the hour—numeric.
s | The second within the minute—numeric.
S | Milliseconds—numeric.
a | The AM/PM marker—text.
k | The hour within the day—a number from 1 to 24.
K | The hour in AM/PM format—a number from 0 to 11.
z | The time zone—text.
' | Escape for text—delimiter.
'' | Single quotes around literals.

As with date formatting, the characters you specify in the pattern are case-sensitive, and the rules relating to the number of repetitions of a symbol apply the same way. Let's look at another example, in Listing 8.14.

Listing 8.14: Time-formatting examples, using both symbols and literals in t he format.
Start example
import java.text.*;
import java.util.*;

public class TestTimeFormat2
{
   public static void main(String args[])
   {
      Date currentTime = new Date();
      SimpleDateFormat format1 =
         new SimpleDateFormat("'Time is:' hh:mm:ss:SS a zz");
      SimpleDateFormat format2 = new SimpleDateFormat("'Time is:' kk-mm-ss");
      SimpleDateFormat format3 = new SimpleDateFormat("'Time is:' KK-mm-ss");

      String timeString1    = format1.format(currentTime);
      String timeString2    = format2.format(currentTime);
      String timeString3    = format3.format(currentTime);

      System.out.println(timeString1);
      System.out.println(timeString2);
      System.out.println(timeString3);
   }
}
End example

Listing 8.14 gives the following results:

Time is: 09:54:41:92 PM EDT
Time is: 21-54-41
Time is: 09-54-41

It uses SS for milliseconds, a for AM or PM, and zz to indicate the time zone. It also includes different options for expressing the hour, namely lowercase k and uppercase K. The former gives the number 21 instead of 9, as expected. However, the latter gives 9, while the documentation implies we should get 8. Possibly this is a bug, since it seems to be the same as lowercase h.

The Default Formatter

You have seen examples of how to create your own formats using SimpleDateFormat, and specify the format on the constructor. (You could change that format on the fly if you wanted, using the applyFormat method.) The Java documentation recommends you do not typically create your own formats. Rather, there are static methods in the DateFormat class (SimpleDateFormat's parent class, a concept covered in chapter 9) that, when called, will return a SimpleDateFormat object. Actually, they are defined to return a DateFormat object, so you have to cast them to a SimpleDateFormat object (a concept also covered in chapter 9).

These methods are getDateInstance, getTimeInstance, and getDateTimeInstance.

They are designed to give you a preset format for dates, times, and date plus time, respectively, that is culturally correct for the country and language of the computer that your Java program is running in. As you know, each country has its own preferred method of formatting dates and times, and if you are writing international software, it is polite to adhere to these standards versus forcing your own country's standard on others. Java gives you a nice easy way to do this, as shown in Listing 8.15.

Listing 8.15: Formatting date and time with country-specific preferences.
Start example
import java.text.*;
import java.util.*;

public class TestDefaultFormats
{
   public static void main(String args[])
   {
      Date currentTime = new Date();
      SimpleDateFormat datefmt =
         (SimpleDateFormat)DateFormat.getDateInstance();
      SimpleDateFormat timefmt =
         (SimpleDateFormat)DateFormat.getTimeInstance();
      SimpleDateFormat dttmfmt =
         (SimpleDateFormat)DateFormat.getDateTimeInstance();
      System.out.println(datefmt.format(currentTime));
      System.out.println(timefmt.format(currentTime));
      System.out.println(dttmfmt.format(currentTime));
   }
}
End example

In this example, each of the three static methods is used to retrieve objects for date, time, and date plus time, in a culturally correct format. The result of running this on Windows NT set for USA and English is the following:

Jun 4, 2000
10:13:05 PM
Jun 4, 2000 10:13:05 PM

You see that you get a short form for the month, which might not be what you want, even if otherwise you can accept the format given to you. There is way to change this, by specifying a parameter on the method calls indicating whether you want a short format, medium format, long format, or full format. The constants defined in DateFormat for these options are SHORT, LONG, MEDIUM, and FULL. In Listing 8.16, you can try each of them to see what you get.

Listing 8.16: Examples of short, medium, long, and full formatting of dates and time.
Start example
import java.text.*;
import java.util.*;

public class TestDefaultFormats2
{
   public static void main(String args[])
   {
      int styles[]    = {DateFormat.SHORT, DateFormat.MEDIUM,
                        DateFormat.LONG,  DateFormat.FULL};
      String names[] =  {"SHORT", "MEDIUM", "LONG", "FULL"};
      SimpleDateFormat datefmt, timefmt, dttmfmt;

      Date currentTime = new Date();
      for (int idx = 0; idx < styles.length; idx++)
      {
         System.out.println("Style: " + names[idx]);
         datefmt = (SimpleDateFormat)DateFormat.getDateInstance(styles[idx]);
         timefmt = (SimpleDateFormat)DateFormat.getTimeInstance(styles[idx]);
         dttmfmt = (SimpleDateFormat)
            DateFormat.getDateTimeInstance(styles[idx],styles[idx]);
         System.out.println(datefmt.format(currentTime));
         System.out.println(timefmt.format(currentTime));
         System.out.println(dttmfmt.format(currentTime));
         System.out.println();
      }
   }
}
End example

Note that getDateTimeInstance requires two parameters, one for the date style and one for the time style. Running Listing 8.16 produces the following:

Style: SHORT
6/4/00
10:43 PM
6/4/00 10:43 PM

Style: MEDIUM
Jun 4, 2000
10:43:57 PM
Jun 4, 2000 10:43:57 PM

Style: LONG
June 4, 2000
10:43:57 PM EDT
June 4, 2000 10:43:57 PM EDT

Style: FULL
Sunday, June 4, 2000
10:43:57 PM EDT
Sunday, June 4, 2000 10:43:57 PM EDT

We recommend you use these static methods to produce your formats for dates and times instead of creating your own formats. (It is, of course, your choice.)


Look Before You Leap

One of the other methods available in the GregorianCalendar class that is worth mentioning is the isLeapYear method. Listing 8.17 shows an example of how you use it. You simply supply the method with the year as an integer parameter. Listing 8.17 gives the following:

Listing 8.17: Coding for leap year.
Start example
   1600 a leap year? true
   1900 a leap year? false
   1976 a leap year? true
   1999 a leap year? false
   2000 a leap year? true

import java.util.*;

public class TestLeap
{
   public static void main(String args[])
   {
      GregorianCalendar date = new GregorianCalendar();
      System.out.println("1600 a leap year? " +
         date.isLeapYear(1600));
      System.out.println("1900 a leap year? " +
         date.isLeapYear(1900));
      System.out.println("1976 a leap year? " +
         date.isLeapYear(1976));
      System.out.println("1999 a leap year? " +
         date.isLeapYear(1999));
      System.out.println("2000 a leap year? " +
         date.isLeapYear(2000));
   }
}
End example

Calculating a leap year is not as straightforward as you might initially expect. For example, it is not as simple as determining if the year is divisible by four. If you need to know if a given year is a leap year, you can count on this method to do it reliably. It is also true, though, that given how much support there is in Java for date manipulations, the need to determine "leap yearness" is perhaps not as necessary as it used to be, when programmers had to do their own date math.


Time Zones

Sometimes, you need to establish the current date or time in an explicit time zone other than the one in which the program is currently running. This functionality is not available in COBOL (unless you use Locales, which we don't discuss here), but Java does offer it. Java supports a TimeZone class in the java.util package, which can be used to instantiate an instance of a particular time zone, as follows:

TimeZone tz_GMT = TimeZone.getTimeZone("GMT");
TimeZone tz_EST = TimeZone.getTimeZone("EST");

Notice how the constructor parameter is simply a string representing a valid, known time zone, such as EST for Eastern Standard Time. To see all the supported time zones, use the static getAvailableIDs method:

String tzs[] = TimeZone.getAvailableIDs();
for (int idx = 0; idx < tzs.length; idx++)
   System.out.println(tzs[idx]);

To get the current time zone for your computer, use the TimeZone.getDefault() method. Finally, once you have a TimeZone object, you can use it in both your GregorianCalendar object to affect manipulations, and in your DateFormat object to affect the time displayed. In both cases, simply use the setTimeZone method.

For example, if you have a Date object, say from instantiating the Date class explicitly or from using the getTime method of a GregorianCalendar object, you might want to display that object's time value for each of your offices around the world. To do this, you create a SimpleDateFormat object as usual, but before using its format method, first call the setTimeZone method with the TimeZone object you wish to use, as shown in Listing 8.18.

Listing 8.18: Coding for different time zones.
Start example
import java.util.*;
import java.text.*;

public class TestTimeZones
{
   public static void main(String args[])
   {
      Date     today = new Date(); // Current date/time
      TimeZone tz1   = TimeZone.getTimeZone("PST");
      TimeZone tz2   = TimeZone.getTimeZone("EST");
      TimeZone tz3   = TimeZone.getTimeZone("GMT");
      TimeZone tz4   = TimeZone.getTimeZone("JST");
      TimeZone tz5   = TimeZone.getDefault();
      SimpleDateFormat formatter =
            new SimpleDateFormat("hh:mm:ss - 'TimeZone = ' z");
      formatter.setTimeZone(tz1);
      System.out.println( formatter.format(today) );
      formatter.setTimeZone(tz2);
      System.out.println( formatter.format(today) );
      formatter.setTimeZone(tz3);
      System.out.println( formatter.format(today) );
      formatter.setTimeZone(tz4);
      System.out.println( formatter.format(today) );
      formatter.setTimeZone(tz5);
      System.out.println( formatter.format(today) );
   } // end main method
} // end TestTimeZones class
End example

This results in the following:

07:59:11 - TimeZone =  PDT
10:59:11 - TimeZone =  EDT
02:59:11 - TimeZone =  GMT
11:59:11 - TimeZone =  JST
10:59:11 - TimeZone =  EDT

Note how the displayed time is different depending on what time zone is used, which is what you wanted. Use the getDefault method to return a TimeZone object representing the time zone of the machine on which the program is running. In Windows, you might have to ensure your time zone is set properly on your machine, using the Date/Time icon in your Control Panel folder.


Summary

This chapter discusses the following aspects of date and time manipulation:

  • COBOL date and time support has been enhanced to help with two-digit-year work, and COBOL on OS/400 has added some significant date and time support that will save much handcrafting of date and time manipulation routines.

  • In Java, the method currentTimeMillis in the System class returns the total number of milliseconds since the base time or epoch.

  • Java has a Date class for retrieving and representing basic date and times.

  • For any kind of date or time manipulations, the GregorianCalendar class is the one to use.

  • To extract parts of a date in Java, use the get method on a GregorianCalendar object.

  • Use the SimpleDateFormat class to get Java to do date formatting for you. It is preferred that you use the getDateInstance, getTimeInstance, and getDateTimeInstance static methods to create your formats instead of creating your own. These return culturally correct formats for the country and language the application is running in.

  • Use the isLeapYear method in the GregorianCalendar class to test whether a year is a leap year.

  • Use the TimeZone class to automatically account for different time zones, given a base time.


Chapter 9: An Object Orientation

Overview

Java is an object-oriented (OO) language. That is a good thing. Really, it is. But what exactly does it mean? This chapter defines "OO" and describes how Java implements it, pointing out the syntax, semantics, and capability considerations. But, to make this information useful, it is important to begin to "think OO." That is quite different than "thinking procedural," as your COBOL background has taught you. And it won't come by reading one chapter of a book.

We warn you, this chapter will probably not make sense to you the first time you read it. However, it will make sense, with time. The hard part about OO is not the "how," it is the "why." So be patient, read the chapter a couple of times, and then move on. Once you start working with Java in earnest, it will start to click. Oh yeah, and go get yourself a book on object-oriented design and analysis for next weekend.

As you read the following pages, keep one thing in mind: the primary goal of OO is reuse of code. By getting a higher level of reuse, you get significant gains in productivity and significant reductions in maintenance costs. This is because you have to write less new code, and the reused code is better tested, due to its higher usage.

If you were designing a language to support reuse as the number-one priority, what might you include to support this goal? Well, for Java and other object-oriented languages, the following traits are most desired:

  • The language should include the capability to write small, self-contained code snippets that do one thing, and do it well. This means allowing all the data and functions that support that one thing to be grouped in one place. Further, the language should have the capability to expose only the functions, and not the internal implementations of those functions. This allows the internal implementation to be changed later without affecting those programs that use it. Ultimately, when you change code, you do not even want to have to recompile those programs that use it.

  • Since it is rare to design one object or snippet of code that does one thing the right way for all situations, the language should have the capability to extend and tailor aspects of that code, while using the remainder as is. Ultimately, this should be done without requiring access to the source code.

  • The languages should include the capability to write code that is easily extendable into the future, as new requirements come along. New additions should be possible without changes to the existing well-tested code base. Ultimately, all existing code should instantly work with the new code, without change.


OO Terminology

It is generally agreed that, for a computer language to be considered object-oriented, it must support the concepts mentioned above. The formal terminology for these concepts is

Encapsulation.
Inheritance.
Polymorphism.

What the heck are these? Read on….

Encapsulation

Java classes, like COBOL modules, offer code reuse and packaging options through the ability to have one compiled piece of code that does a self-contained set of functions, such as managing a stack. This unit is reusable as-is by many others. Classes are the basic building blocks of all object-oriented languages. Putting variables (data) and code together into a easily reused "black box" that others can access through specifically designated interfaces is called encapsulation. It is an appropriate term because you encapsulate the data, and allow access only via related functions.

Consider a stack example. Suppose one day you decide to change the underlying data structure from an array to a dynamic linked list. No problem. You can make this change without affecting all users of the code, if you have properly restricted access to the internal variables by marking them as private. You do this by restricting users to, say, the public methods push and pop. How you implement those methods is of no consequence to those who use them.

Because of this ability to encapsulate the data (variables) from direct and unpredictable access, you often see graphics that show objects as a circle. The data or variables are in the middle, and the methods are around the outside, as shown in Figure 9.1.

Figure 9.1: Encapsulation of methods.

This is a way of conveying that the class-level or instance variables in the center are protected from direct access by the methods. That is, you must use one of the methods to access the variables. This helps ensure data integrity, and gives the class designer control over future changes.

There is also a second way of looking at, or describing, encapsulation. It is the ability to capture in one place both the data (variables), and the functionality (methods) that works on that data. Thus, instead of having a data structure declared in one place in an application, with disparate code scattered throughout the application that reads, writes, and manipulates that data structure, you keep the data structure and only the methods that work on it nicely encapsulated in one place. Anyone looking to use that data is forced to go through your methods, even just to read it. Again, there are big payoffs in maintenance for this, as you isolate all related functionality in one place, and use it repeatedly not only in one application, but across multiple applications.

Remember the work to find and fix all the two-digit-year references in the Y2K problem? Imagine if, like Java, all dates were objects of one class, with methods to do things like compare two date objects. If that were the case in COBOL, you would have had to change only a single class to do the century windowing, and every application would "magically" have been fixed. They would not even have required a recompile.

As another example, imagine encapsulating together in one place all the variables and methods to deal with things like employees, salaries, taxes, commissions, or things your company sells. By turning these into classes that are well-designed and well-tested, you can successfully reuse them in many places in one application, and many applications.

Later, when the employee serial number has to increase from five digits to six, or when the salary grid, tax rate, or commission rate changes, or when the attributes of things you sell change, you can make the change once, in one place, and all will benefit. This is similar to the habit of putting volatile information like tax rates into database tables, but now you can isolate not just values that might change, but the algorithms as well. This is because classes contain both variables (data) and methods (algorithms). Note that you might still place the values in a database table, and design the class constructor to read these values from the table.

So, do you have encapsulation in COBOL? To a degree, yes, you do. Any COBOL program can offer encapsulation. For example, you might have a program that returns information about a given customer. Any application that needs to get this information can be forced to reuse this common program. Further, data items in the program are by default private, unless you explicitly mark them otherwise, as EXTERNAL.

The difference between Java classes and COBOL programs is that you can only have one "instance" of a reusable COBOL object per application, and hence only one set of values for its data items. With Java, as you now know, you can have multiple instances per application, by simply using the new operator as often as needed. Although you might be having trouble grasping the value of this concept, it will come with time. We have been writing COBOL applications for years that work on a single record or row from a database at a time, and after each is processed, we refresh the data items with the values from the next record. In Java, we can still do this if we want, but we gain the added flexibility of being able to work on multiple records simultaneously.

For example, your Customer class can be instantiated multiple times, once for each customer your application is currently working with. You have seen some other examples of the power of objects—namely, strings and dates—in previous chapters. These are classes that really benefit from the ability to have multiples active at once—what good would it do to be restricted to one string value at a time?

User-interface programming also makes extensive use of multiple instances of classes, as discussed in chapter 12. For example, there is a class for a button on the screen, and you can instantiate multiples of it, such as one labeled "OK" and one labeled "Cancel." The label for each instance is stored internally in an instance variable, which you query and set via the setLabel and getLabel methods.

A good example of encapsulation is the database itself. Database files are typically created with very limited public access, and end users are forced to go through your programs to access the database. They cannot edit the data directly, using data-access tools. Typically, your programs use adopted authority to gain entry to the data. It might be helpful to think of the database, then, as being like your private variables. In contrast, your programs are like the public methods through which access to the variables is restricted.

Inheritance

Yes, encapsulation is a good thing. However, with enough patience and careful design, you can get the main benefits of it even in COBOL. So, why all the fuss in the industry about OO languages? OO gets so much attention because of another capability of classes—the ability to be extended (also known as subclassed). This ability that classes have is unique. There is no analogy in COBOL (unless you go to OO COBOL, which is described in appendix C). As you will see, it can offer great rewards once you get the hang of it.

The examples you have seen so far of Java classes have been defined from scratch. That is a common use for Java. However, you will often find yourself in another situation. Suppose you want a version of an existing class that is only slightly different, so you don't want to copy and maintain a second, slightly altered version. This can be done! You simply define your new class and specify that it extends the original class. Your new class will inherit all the variables and methods of the parent class.

Well, not quite. It does inherit them all, but the new code in the new class is not allowed to access any variable marked as private, or to invoke any method marked as private. Unmarked ("package") and public variables and methods, however, are accessible.

To show you an example, we need to start with a parent class, which will subsequently be extended to create a second class. Listing 9.1 shows a class that represents an employee, capturing some common information and functions.

Listing 9.1: A parent class that represents an employee, with common information and functions.
Start example
import java.util.*; // for GregorianCalendar class
import java.math.*; // for BigDecimal class

public class Employee
{
   protected int               id;        // unique employee ID
   protected String            name;      // name of employee
   protected GregorianCalendar startDate; // date employment started
   protected int               level;     // promotion level
   protected int               dept;      // ID of department
   protected BigDecimal        salary;    // current salary

   /**
   * Constructor. Given an unique employee ID, this will read this
   *              employee's information from the employee database
   */ public Employee(int id)
   {
      this.id = id;
      // not shown: reading employee info from database
   }
   public int getID()
   {
      return id;
   }
   public String getName()
   {
      return name;
   }
   public int getLevel()
   {
      return level;
   }
   public int getDept()
   {
      return dept;
   }
   public BigDecimal getSalary()
   {
      return salary;
   }
   public int returnYearsOfService()
   {
      int currentYear = (new GregorianCalendar()).get(Calendar.YEAR);
      int startingYear= startDate.get(Calendar.YEAR);
      return currentYear - startingYear;
   }
   public BigDecimal getBiWeeklyPay()
   {
      BigDecimal biweeklyPay = salary.divide(
         new BigDecimal(26), BigDecimal.ROUND_HALF_UP);
      return biweeklyPay;
   }
}  // end class Employee
End example

The functionality of class Employee is not important for the discussion here, so we won't dissect it, but it is very simple anyway. You might wonder why the variables were not made private, but instead were made protected. This modifier (discussed in more detail later in this chapter) allows child classes, and child classes only, access to the variables.

Now, imagine this is only the base functionality you want to capture for all employees, but for certain types of employees, you need to capture additional information as well. For example, you need to know the commission rate of sales clerks. Other than that, sales-clerk employees have all the same functionality as other employees. Listing 9.2 shows how you can accomplish this.

Listing 9.2: Extending a class adds additional variable and method to the class.
Start example
import java.math.*; // for BigDecimal class

public class SalesClerk extends Employee
{
   protected BigDecimal commissionRate;

   /**
   * Constructor. Just call parent's constructor
   */
   public SalesClerk(int id)
   {
      super(id);
      // not shown: retrieving commission rate from database
   }
   public BigDecimal getCommissionRate()
   {
      return commissionRate;
   }
} // end SalesClerk class
End example

The example in Listing 9.2 shows a new class, SalesClerk, that extends the parent class, Employee. Because SalesClerk is an extension, it inherits all the methods and variables of the parent class, in addition to the method that is unique to this class, getCommission Rate. To support this method, an additional variable is added, as well. A constructor also had to be created for this new class because constructors are not inherited. However, as you see, there is simple syntax for just invoking the extended class's constructor, using the special method, super. (You'll see more about constructors and inheritance later in this chapter.)

We say our new class is a child class of the class it extends, which is called its parent class. This is a parent/child relationship because the child class inherits attributes from the parent, as in real life. Alternatively, we can also call our new class a subclass of the class it extends, which we then call the superclass. This is the origin of the keyword super. Sometimes, the parent class is also called the base class. The key to this terminology is the idea of a hierarchy, with the first class at the top, and the extending class at the bottom.

This ability to create new classes by simply extending existing ones and adding functionality helps tremendously in reaching the goal of reuse. For example, you might think it would be just as easy to add commission-rate information to the Employee class and simply ignore it for noncommissioned employees. That is true, but it has disadvantages:

  • It wastes space for noncommissioned employees.

  • It is not as elegant; it muddies up the Employee code with special-case code and data.

  • You might not have the source code to Employee. If you bought this class off-the-shelf, you might only have the bytecode, but you can still extend it.

Of course, if all you could do is add methods in the child class, you might have a problem. Sometimes, one of the methods in the parent class does something that is not appropriate or sufficient for the child class. So, Java gives the ability to override any method in the parent class, simply by redefining it in the child class. You must use exactly the same name and parameters (signature) as the original, but with different logic.

Removing existing methods is also possible, achieved by overriding the original method signature, but then supplying empty braces for the logic ("no operation"). You might expect a more elegant and formal method for removing methods from the parent class. Actually removing methods would cause a problem with the next concept to be discussed—polymorphism—so you are left with only this one convention. Also, the intention for extending classes is for adding or refining functionality, not removing it. A general design rule is that if you find yourself needing to remove a method, you probably should not be designing this class to extend that particular parent, or you should not have put that method in the parent class in the first place.

Note

When you override a method in a class that uses extends, the signature is important. Keep in mind that two methods with the same name are considered different if they take different parameter lists. To override a method, you must match both its name and its parameter list in the new class. The return type is not part of the signature. If you do override a method (by specifying a new method with the same name and parameter list as in its parent class), but specify a different return type, you will receive an error from the Java compiler because of the ambiguity.

To illustrate the option of overriding a method in the parent class, let's revisit the Sales-Clerk class. One of the methods it inherits from the Employee class is getBiWeeklyPay. This method's algorithm (divide salary by 26) is fine for noncommissioned employees, but for commissioned employees, you have to also include the commission for the last two weeks. To this end, Listing 9.3 redesigns the SalesClerk class to override the

getBiWeeklyPay method from the Employee class.

Listing 9.3: Overriding a method in the parent class.
Start example
   import java.math.*; // for BigDecimal class
   public class SalesClerk extends Employee
{
   protected BigDecimal commissionRate;

   public SalesClerk(int id)
   {
      super(id);
      // not shown: retrieving commission rate from database
   }

   public BigDecimal getCommissionRate()
   {
      return commissionRate;
   }
   /**
   * Return bi-weekly pay for this employee. Overrides parent method.
   */
   public BigDecimal getBiWeeklyPay()
   {
      BigDecimal biweeklyPay = salary.divide(
            new BigDecimal(26),BigDecimal.ROUND_HALF_UP);
      BigDecimal sales = new BigDecimal(0);
      // not shown: querying sales figures from sales database
      BigDecimal biweeklyCommission = sales.multiply(commissionRate);
      biweeklyPay = biweeklyPay.add(biweeklyCommission);
      return biweeklyPay.setScale(2, BigDecimal.ROUND_HALF_UP);
   }
} // end SalesClerk class
End example

This second version of the SalesClerk class adds the method getBiWeeklyPay. There is a method of the same signature in the parent class, so that method has been overridden to change the algorithm for calculating biweekly pay to include the clerk's commission for the previous two week's worth of sales. As mentioned in chapter 5, the BigDecimal class is the preferred way to store and manipulate fixed-decimal numbers such as dollar values. The setScale method needs to be used on the returned value because the multiply method gives a new BigDecimal object with a scale (decimal point) that is the sum of the scales of the two operands.

So, this is good, this overriding capability. You can really tailor parent classes to explicitly suit your needs. The one little downside you might have noticed is that you had to duplicate the code to calculate biweekly pay. That line of code is the same in the Employee class and the SalesClerk class. This is not a terribly big deal, but it sure does go against the stated goal of reuse.

You might try to get around this by changing that line of code in the SalesClerk class to instead call the method getBiWeeklyPay in the parent class. However, this will not work because the override method is also called getBiWeeklyPay—as per the rules of overriding—so a call to getBiWeekPay only result in a recursive call! You want to reduce redundant code, not create infinite loops.

Luckily, Java has taken care of this problem for you. It allows you to call a method in a parent class by explicitly telling Java to use the parent's version of the method, not the local version. This is done by simply prefixing the method call with super. (the word super followed by a period). The keyword super in Java denotes the parent object, as opposed to the keyword this, which denotes the current object. Listing 9.4 shows the new version of getBiWeeklyPay, using this syntax to avoid the redundant code (which is commented out). This simple but powerful syntax allows you to not only override methods in their entirety, but also to augment parents' methods with additional logic. Really cool!

Listing 9.4: A new version of getBiWeeklyPay.
Start example
public BigDecimal getBiWeeklyPay()
{
   // BigDecimal biweeklyPay = salary.divide(
   //         new BigDecimal(26),BigDecimal.ROUND_HALF_UP);
   BigDecimal biweeklyPay = super.getBiWeeklyPay();
   BigDecimal sales = new BigDecimal(0);
   // not shown: querying sales figures from sales database
   BigDecimal biweeklyCommission = sales.multiply(commissionRate);
   biweeklyPay = biweeklyPay.add(biweeklyCommission);
   return biweeklyPay.setScale(2, BigDecimal.ROUND_HALF_UP);
}
End example

Are you in one of those shops that buys a package from a software vendor and then tailors it heavily? What is the problem with this? The problem is you have a major headache every time the vendor ships a new release—you have to carefully extract your changes, and then reapply them to the new code base. What a pain! With inheritance, you would be out of that business. Rather than changing the vendor's actual code, you could simply extend it and selectively override the methods for which you need to change the code. Then, you can easily update the vendor's code base with a new release without doing the "code-merge dance."

Deep Inheritance

The keyword super is always valid in Java because every class inherits from some other class. Even parent classes, which might have no explicit extends keyword, implicitly extend from the Java core class, Object. This is a powerful feature. It means that all classes have the same ultimate ancestor.

If you look up the Object class in the JDK documentation for the Java-supplied package java.lang, you will see it has a number of supplied methods that you might find useful. Because all classes ultimately inherit from Object, every object you create or reference—regardless of the class type—will have this default set of methods. This tells us something else, too: there is no limit to the size of an inheritance tree. All classes implicitly extend Object, and other classes may extend these, and so on. Pictorially, the hierarchy is usually shown with the class names inside square boxes and an arrow pointing from the child class up to the parent class. The inheritance tree for the SalesClerk class is shown in Figure 9.2.

Figure 9.2: An example of a sim-ple classes inheriting from a parent class

You can go as deep as you wish, but keep in mind that super accesses only the immediate parent. Another important point is that two different classes can inherit from the same parent class. If there were a second child class called, say, Manager, which extended the Employee class, the hierarchy tree would look like Figure 9.3.

Click To expand
Figure 9.3: An inheritance hierarchy tree with multiple inheritance hierarchy tree..

A class browser is a tool you will come to rely on when writing Java code (and any OO code). This tool shows the inheritance tree for a selected class. All worthwhile Java development environments, including VisualAge for Java, have such a tool. In VisualAge for Java, it is incorporated into the Integrated Development Environment (IDE) and is easily accessed by selecting the Hierarchy tab when editing a particular class. Figure 9.4 shows what this tab looks like for the class hierarchy in the Sales-Clerk example.

Click To expand
Figure 9.4: VisualAge for Java's class hierarchy browser.

Polymorphism

No, polymorphism is not a denture cream! When you define a class that extends another class, and then override one of the inherited methods with a new implementation of that method, you are practicing polymorphism. (Aren't you naughty!) Polymorphism refers to the fact that calling the same method on two objects can have different results, if the objects come from different classes in the same hierarchy. Let's think for a minute what it would mean to use the new child class, SalesClerk. Eventually, this will lead to an illustration, and then a definition, of polymorphism.

Listings 9.3 and 9.4 overrode the method getBiWeeklyPay. Imagine now that you are creating the biweekly payroll application for your company, and your application must instantiate instances of Employee for all employees. For sales clerks, you will instantiate SalesClerk objects, not Employee objects. The first question you might have is, "Where and when do I make the decision about which class to instantiate?" This is just a style question, and one option might be after you read the database record for an employee and determine whether this employee is a sales clerk. However, that does not fit well with the Employee class, because it reads the database in the constructor, at which point it is too late to suddenly change the instantiation from Employee to SalesClerk.

So, the Employee class needs to be changed, by following a common practice for these situations. You supply a static method in the Employee class that reads the next employee record (keyed by employee ID) from the database, and looks at the information from the database to determine which of the two classes to instantiate—Employee or SalesClerk. It then actually instantiates that class and returns the new object to the caller.

Object Factories

This type of static method, which does the instantiation of an appropriate object (versus letting users call new directly), is called an object factory method. The idea is that it is like a factory, whose job is to create objects, doing all the decision-making about which class to use. This design is one of a number of common designs that researchers have discovered when looking through many object-oriented systems to see how people solved common problems. The results of this research are called design patterns, which were first made immensely popular by Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides in their 1995 landmark book Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley). This book was based on C++, but can be applied easily to Java. The object-factory design described here is actually slightly more complex in reality (but not much), and is known officially as the factory method pattern.

Let's change the Employee and SalesClerk classes to support the object-factory method. Since that method will read the database only after instantiating the appropriate object, you have to change constructors to take in all the parameters for all the instance variables. First, Listing 9.5 shows the updated SalesClerk class with its new constructor.

Listing 9.5: The SalesClerk class with a new constructor.
Start example
import java.util.*; // for GregorianCalendar class
import java.math.*; // for BigDecimal class

public class SalesClerk extends Employee
{
   protected BigDecimal commissionRate;

   /**
   * Constructor. Just call parent's constructor
   */
   public SalesClerk4(int id, String name, GregorianCalendar startDate,
                     BigDecimal salary, int level, int dept,
                     BigDecimal commissionRate)
   {
      super(id, name, startDate, salary, level, dept);
      this.commissionRate = commissionRate;
   }

   // *** REST HAS NOT CHANGED SO IT IS NOT SHOWN ***

} // end SalesClerk class
End example

It's a simple enough change. You just take everything in as parameters and pass all but the unique stuff to the parent's constructor. The comment about reading the commission rate from the database is removed, since that will be done now in the new factory method. Let's get to that now. Listing 9.6 shows the changed parts of the Employee class, namely, the changed constructor that takes everything as parameters and the new createEmployee factory method.

Listing 9.6: A factory method to instantiate and return an object of the appropriate type.
Start example
import java.util.*; // for GregorianCalendar class
import java.math.*; // for BigDecimal class

public class Employee
{
   protected int               id;        // unique employee ID
   protected String            name;      // name of employee
   protected GregorianCalendar startDate; // date employment started
   protected BigDecimal        salary;    // current salary
   protected int               level;     // promotion level
   protected int               dept;      // ID of department

   /**
   * Constructor. Given all the information, this instantiates one Employee
   */
   public Employee(int id, String name, GregorianCalendar startDate,
                  BigDecimal salary, int level, int dept)
   {
      this.id = id;
      this.name = name;
      this.startDate = startDate;
      this.salary = salary;
      this.level = level;
      this.dept = dept; }
   /**
   * Factory method for creating appropriate Employee object. Reads the next
   * employee record from the database and creates/populates appr. object.
   */
   public static Employee createEmployee()
   {
      // local variables that will be populated from database record
      int               id=0;           // next key value
      String            name=null;      // name of employee
      GregorianCalendar startDate=null; // date employment started
      BigDecimal        salary=null;    // current salary
      int               level=0;        // promotion level
      int               dept=0;         // ID of department
      BigDecimal        commissionRate=null; // Non-zero for commissioned emps
      char              type=' ';       // what type of employee??
      boolean           endOfFile = false; // no more records?
      // not shown: reading next keyed employee record from database
      if (endOfFile)
         return null;
      else if (type == 'S') // sales clerk?
         return new SalesClerk(id,name,startDate,salary,
                                       level,dept,commissionRate);
      else
         return new Employee(id,name,startDate,salary,level,dept);
   }

   // *** REST HAS NOT CHANGED SO IT IS NOT SHOWN ***

}  // end class Employee
End example

This is nice, isn't it? All the decision-making has been nicely encapsulated (there's that word again) into the class, so users don't have to think about different types of employees at all. Notice, though, that the factory method's return type is declared to be Employee. That is interesting, is it not? You might have been wondering what type you could declare it as, since it will be returning either an Employee object or a SalesClerk object. We just chose one of the two, seemingly at random.

Will this compile? Yes, it will. There is a very good reason it will, and it certainly would not compile if we did not make the correct choice. We explicitly chose the parent class as the return type. Had we chosen the SalesClerk class as the return type, the compiler would complain about the statement that returns an Employee object.

Remember that Java is a strongly typed language, so the rule is that you must return only values of the type declared as the return type, or for class types, objects of that class or a child of that class. This is the key. Java (and other object-oriented languages) allows you to declare a variable (or return type or parameter) to be of one class type, and at runtime refer that variable to objects of any child classes of that declared type. Thus, specifically for parent/child classes, the declared type can be different than the runtime type of the object actually referred to.

This, then, is what ultimately produces polymorphism. Let's now imagine (and write) the code to use this great factory method. The payroll application needs to call the factory method to instantiate appropriate objects for each of the employees. If your company is small, you might choose to collect all of these objects into an array and then process the array. If your company is too big for that to be efficient (because it takes too much memory), you could process each employee object as you get it, and let the garbage collector subsequently swallow it up again.

Since the company for this fictional example doesn't exist, it is small, so you can create an array and populate it by calling the factory method. Assume, for simplicity, that you know how many employees you have, but if you did not, you could just use a vector instead of an array. Note that the array will contain both Employee objects and SalesClerk objects, since there are both commissioned and noncommissioned employees.

So what type will the array be declared as? Employee—right, you get it. Even though it is declared as type Employee, you can put into it objects not only of class Employee but also of any class that is a child of Employee (that is, which extends Employee). Here is the start of the application:

static final int EMPS = 100; // This would be queried or we'd use a Vector
Employee employees[] = new Employee[EMPS+1];
for (int idx=0; idx < employees.length; idx++)
   employees[idx] = Employee.createEmployee(); // reads next database record

Finally, let's walk the array of employee objects, and for each one, perform the payroll operation by writing a check. How much should you make the check for? That's easy!

You just call the getBiWeeklyPay method, which both classes support. But wait, you say. You need to determine, for each object, if it is of type Employee or type SalesClerk, and then call getBiWeeklyPay in Employee for the former case, and getBiWeeklyPay in SalesClerk in the latter.

How do you determine the object type at runtime? There is a way we will describe later (the instanceof operator). How do you force Java to call a method in a specific class? There is a way we will describe later (casting of objects), but it's too much work for now. And why override the parent's getBiWeeklyPay method in the child SalesClerk class, anyway? Is there any value in it?

Here is the payroll application, given an array that is created and populated as shown previously:

for (int idx=0; idx < employees.length; idx++)
{
   String employeeName = employees[idx].getName();
   BigDecimal checkAmount = employees[idx].getBiWeeklyPay();
   PayRoll.writeCheck(employeeName, checkAmount); // not shown
}

The interesting line that answers all of the questions is the one that calls getBiWeeklyPay on the current array element. Remember that the array's elements are declared to be of type Employee, so what happens when an element in the array of type SalesClerk is encountered? This, friend, is polymorphism! You have seen it with your own eyes. The answer is that for SalesClerk objects, the SalesClerk version of getBiWeeklyPay will be called, while for Employee objects, the Employee version of getBiWeeklyPay will be called. Java elegantly knows which version of the overridden method to call, based on the runtime object type.

To complete the example, you might place that code into a nice little method of its own, for modularity purposes, which would take the array of employees as a parameter. Here is what that might look like:

public static void doPayroll(Employee employees[])
{
   for (int idx=0; idx < employees.length; idx++)
   {
      String employeeName = employees[idx].getName();
      BigDecimal checkAmount = employees[idx].getBiWeeklyPay();
      PayRoll.writeCheck(employeeName, checkAmount);
   }
}

Polymorphism, then, is the ability to call one of many different versions of a method at runtime, depending on the runtime object type. It's a simple idea. In Greek, it means "many faces" or "many shapes," and it reflects the fact that what you see coded at declaration time is not necessarily what will happen at runtime. Look at the doPayroll method. If it is all you could see, you would have to assume that the getBiWeeklyPay method in the Employee class is called for every object or element in the array. You don't see the class name SalesClerk anywhere, yet, at runtime, the code ends up calling getBiWeeklyPay in the SalesClerk class for SalesClerk objects. It's like magic.

There is a fundamental concept at work here, which we've already mentioned:

An object variable's declared type (at compile time) can differ from its actual type (at runtime). At runtime, the variable can actually refer to an object of any class below the declared class in its hierarchy tree.

This concept, together with the inheritance ability to override methods, gives the polymorphic behavior where the actual method called at runtime will be one of multiple possible candidates, depending on the runtime object types. This reduces the amount of conditional "if this, else that" logic you have to write. Rather than sprinkle your code with all the different cases, you only have to write the different-case code in one or more child classes, and then write one little bit of conditional logic to determine which class to instantiate. After that, your code does not change, and all the different cases are automatically handled.

What if the class of the runtime object did not have an override of that method? In terms of the previous example, what about the call to getName, a method that does not exist in the SalesClerk class, but does exist in Employee? The Java runtime works like this: first, it looks for the method with that exact signature in the class of which the runtime object is a type. If the runtime does not find the method there, it looks in that class's parent class, and so on up the hierarchy. In this case, it would find the method in the parent class, Employee.

If it does not find the method anywhere, even in the universal parent class Object, you will get an error and do the Java equivalent of a program abend. But, of course, the javac compiler would have caught it first. If the method exists in more than one class, the version of the method in the lowest class in the hierarchy—starting at the object's actual runtime class type—always wins.

What if the method being called only exists in one or more of the child classes, but not in the parent class? If the object's compile-time type is the parent class, the compile will fail—the same hierarchy check is made at compile time—but from the compile time class up. In the payroll example, if the method is not found in the declared parent type class of Employee, that is an error. This makes sense, since you are using a parent class at compile time because you are certain that the method exists in all the child classes, as inherited methods from a parent class always do. If the method does not exist in the parent class, it may not exist in the runtime classes either, as far as the compiler is concerned. Confused? One rule can help: The compiler (versus the runtime) can only look up the inheritance tree, not down the inheritance tree, when it is looking for methods.

Now you can see why no formal language construct exists for a child class to remove methods from its parent class. The concept of polymorphism requires the compiler to assume that a method defined in a parent class is always available in its lower, or child, classes. Inheritance guarantees this. Even if you do not override the method in the child class, it will inherit it from the parent class (unless it is defined as private). Thus, the best you can do is override the parent's method and make it do nothing. It still exists, it just has no functionality. You also cannot restrict a method's access rights beyond what is defined at the parent level. For example, you cannot override a method in a child class and change it from a public method to a private method. The reverse is permitted.

Determining Java's Runtime Object Type

In Java, you can always determine an object's class type at runtime by using the great-grandparent class Object, from which all classes inherit. Object, and hence all classes, has a method called getClass, which returns a special java.lang.Class object. This, in turn, has a method called getName that returns a string with the name of the object's class, like this:

String classname = myObject.getClass().getName();


Inheriting More

As you have seen, inheritance is the key to object orientation. You now need to spend a little more time learning some additional syntax and rules that apply to the art of inheritance.

Protected Access

You have already seen examples of the accessor modifiers that can be specified for methods, such as public:

public void myMethod()
{
   ...
}

There are a total of three such accessor modifiers, along with a fourth that is implicit if no modifier is specified, as shown in Table 9.1.

Table 9.1: Java Modifiers.
Java | Description
private | Only accessible by methods in this class.
package (default) | Only accessible by other methods in this package. These include child classes, but only child classes in this package. This is the implicit default.
protected | Only accessible by methods in this class or its child classes. These child classes can be in this package or another package.
public | Accessible by all.

Now, finally, you can see the reason for having four different access options. In Java, you can divide the world (users of your method) into the following categories:

  • Methods in this class (private).

  • Methods in other classes in this package only (package).

  • Methods in child classes in any package (protected).

  • Methods in any class in any package (public).

In order to restrict the usage of your method to one of these four categories, choose the indicated modifier keyword. This allows access only by methods in that category or above. That is, the access rights are cumulative:

  • private = only code in this class can call this method.

  • package = private + all other classes in this package.

  • protected = package + child classes in any package.

  • public = protected + other packages (the world).

When would you use protected? This is designed explicitly for cases where you want the method to be private (for internal use only) to your class and any "family members"—that is, any classes that choose to extend your class into child classes. Typically, if you are defining a class to be extendible (the default, but as you will see, you can prevent it), then you will use the following:

  • private or protected for all instance variables.

  • protected for all helper or internal-use methods (versus private).

  • public for methods for use by the outside world.

Constructor Access

By the way, you can flag constructors as public, or leave them as package. Typically, you do the latter when you don't care to let the public at large instantiate objects of the class using the new operator. Perhaps you have a helper class that is only needed by code in this package. Or perhaps you have used the factory-design pattern described earlier in this chapter. In this case, you want the public to call your factory method to create the object, not use new directly.

Preventing Inheritance

There are times when you will want to completely restrict the ability of another programmer to extend your class and override or replace certain of your methods. From a security point of view, you might have very sensitive methods that you want to ensure cannot be maliciously or unwittingly replaced. For example, the getBiWeeklyPay method for an employee object might be an example of a sensitive method.

Notice the difference between using and overriding. You do not mind child classes using this method—you want this, because it hides all the sensitive calculations. However, you do not want child classes overriding the implementation of this method. For integrity and security, it is important that all code that invokes this method get to the one and only implementation of it, not some other implementation farther down the inheritance chain (thus preventing a Trojan horse kind of attack).

Final Methods

You can prevent child classes from overriding a specific method in Java by using the final modifier keyword on the method:

protected final BigDecimal getBiWeeklyPay()
{
   ...
}

The final modifier tells the compiler that no child subclass is allowed to override this method with its own implementation. The compiler will enforce this, as will the runtime. In addition to security benefits, final also offers performance benefits. It is a clue to the compiler that this method will not be participating in polymorphism, so the compiler has the option of inlining the method. Inlining involves turning calls to the method into static versus dynamic calls (that is, determining and recording the address of the method at compile time, rather than waiting for runtime). It is in your best interest to review your protected and even public methods for opportunities to flag them as final.

Note

Methods that are not final are occasionally referred to as virtual.

Final Classes

Specifying final on each method that you want to protect or perform more efficiently (and that you are sure will not be overridden), is a good, fine-grained form of restriction. There might be a class that you decide is entirely a "leaf" class (on the hierarchy tree), which will never be overridden, so, you want to easily flag that entire class as final. In other words, you want to prevent others from defining new classes that extend this class (preventing others from defining child classes). You can do this in Java, too. Just use the final modifier at the class level, and all methods inside the class are automatically designated as final by the compiler.

In the SalesClerk example, you might decide that no other child classes are anticipated to extend SalesClerk and so, for efficiency, it is declared as final.

public final class SalesClerk extends Employee
{
   ...
}

Enforcing Inheritance

The previous section discusses how to prevent child classes from overriding a particular method, as well as how to prevent child classes altogether for a particular class. There are times when precisely the opposite is required. You might have defined a class hierarchy in which the root parent class will never be instantiated directly. Instead, only child classes of this class will be instantiated. This is common. You find yourself abstracting out common behavior into parent classes whose role is to ensure the children all have common functionality. These parent classes may never be instantiated into objects themselves, as it won't make sense.

For example, consider a classic OO example: shapes. Imagine you want to create classes representing triangles, squares, and circles. The reason for these separate classes is that the graphic systems need to be able to work with multiple shapes simultaneously, doing operations on them such as shading, drawing, and so on. Imagine simple "paint" software needing these types of objects, for example.

The palette would contain triangle, square, and circle objects, which the user could drop on the canvas, and resize and color as desired. Each time a user drops one of these on the canvas, the code will simply instantiate an object of the Triangle, Square, or Circle class to represent that object on the canvas. It would then call the draw method on it to render it on the screen. Wouldn't it be nice if all the operations possible on the objects mapped to methods on the objects! However, how can you just call draw, paint, or fill methods on these objects generically, when the algorithms to do these operations are different for each shape? For example, it is a decidedly different algorithm to draw a circle than to draw a square. Even computing the surface area of circle is different than computing the surface area of a square.

Polymorphism to the rescue! Computer programs such as paint utilities (or any program with a palette of parts, and a palette or toolbar of operations you can perform on those parts) are perfect matches for the power of OO. The trick is to create a base, or parent, class for all the parts, and put in that parent class methods for all the operations that the tool supports on the parts, such as draw, fill, rotate, and so on. Then, for each separate part type, create a child class of that one parent class and override all the methods in it to do the unique algorithm needed for that particular part. Once this is done, the actual code for the GUI program is easy. When the user chooses an operation to perform on the selected parts, just call the corresponding method for that operation, on all the selected objects. The correct algorithm for each part type will "magically" get invoked.

Listing 9.7 is an example of a parent class called Shape. For simplicity it has only shown a single method in its base class, called computeArea, which returns the area of a given shape.

Listing 9.7: A parent class with a method to compute the area of a shape.
Start example
/**
* Base class for two dimensional shapes
*/
public class Shape
{
   protected double height=0;
   protected double width =0;
   protected double radius=0;

   /**
   * Compute the area of the shape.
   */
   public double computeArea()
   {
      return width * height;
   }
} // end class Shape
End example

Listings 9.8, 9.9, and 9.10 show three child classes of Shape for the explicit shapes Circle, Square, and Triangle. They each supply their own override of the method computeArea, with the unique equation for that geometric shape.

Listing 9.8: A class to compute the area of a circle.
Start example
public class Circle extends Shape
{
   public Circle(double radius)
   {
      this.radius = radius;
   }

   public double computeArea()
   {
      return Math.PI * (radius * radius);
   }
} // end class Circle
End example
Listing 9.9: A class to compute the area of a square.
Start example
public class Square extends Shape
{
   public Square(int width)
   {
      this.height = this.width = width;
   }

   public double computeArea()
   {
      return width * height;
   }
} // end class Square
End example
Listing 9.10: A class to compute the area of a triangle.
Start example
public class Triangle extends Shape
{
   public Triangle(int base, int height)
   {
      this.height = height;
      this.width = base;
   }

   public double computeArea()
   {
      return (width * height) / 2;
   }
} // end class Triangle
End example

Notice that, in addition to a unique implementation of the computeArea method, each child class also supplies its own constructor, accepting the necessary parameters for that shape. For GUI builders like the paint utility, you would also have to supply a default constructor that constructed itself with default values for the parameters, since the generic code in the utility program would not want to have to know about all the different constructor signatures. (Java beans also have this requirement for this very reason, as discussed in chapter 14.)

These examples put all the possible instance variables length, width, and radius in the parent class. Alternatively, you could have chosen to put them explicitly in the child classes that needed them—but only if no methods in the parent class had reason to access them. Ok, so it is not terribly exciting only having a computeArea method, but imagine lots of other cool methods as well, especially a draw method (which is usually called paint, actually). Listing 9.11 is a little test of the three child classes to see if they work as expected.

Listing 9.11: A class to test and display the results of the shape-computing classes.
Start example
public class TestShapes
{
   public static void main(String args[])
   {
      Shape shapes[] = new Shape[3];
      shapes[0] = new Circle(20);
      shapes[1] = new Square(4);
      shapes[2] = new Triangle(4,5);
      printAreas(shapes);
   }

   public static void printAreas(Shape shapes[])
   {
      System.out.println();
      for (int idx=0; idx < shapes.length; idx++)
          System.out.println(shapes[idx].computeArea());
   }
} // end class TestShapes
End example

Running TestShapes from Listing 9.11 produces the following output:

1256.6370614359173
16.0
10.0

The printAreas method is a completely generic method that, given an array of Shape-class child objects, can calculate the surface area for each of them. It does this correctly for circles, squares, and triangles, even though it knows nothing about the classes for these shapes. This is another example similar that highlights the power of polymorphism.

Imagine that you needed to redraw the canvas full of these shapes after the user minimizes and restores a window. You would simply write a drawAll or paintAll method, and pass to it the array of all shapes currently on the canvas, and it would just walk the array calling draw for each one. Now, suppose you decided to support a new shape, say polygons. How much work would that take? Not much! Just create a new class called Polygon that extends Shape, override the necessary methods, and you are done! Very nice.

Look at that Shape parent class again, in Listing 9.7. There is something not very nice about it: code in the method computeArea. There really should not be any code there because every child class should override that method to supply a proper algorithm specific for that child's geometric shape. Otherwise, you might forget to override this method in a child class, say in a few months, when you add the Polygon class. That would result in using the parent algorithm for computing the area, which is probably wrong for that child.

You need to fix this, so you can reduce the risk of that bug happening. You need to force all child classes to override this method, and thus delete that default code. Let's see how…

Abstract Methods

You might think you can just omit from the parent class any method that you want all child classes to implement anyway, such as computeArea. Well, you can't. That would break the polymorphism, and in this case, cause the TestShapes class in Listing 9.11 to not compile. The error would look like this:

D:\bookj4c\samplesrc\ch9>javac TestShapes.java
TestShapes.java:16: Method computeArea() not found in class Shape.
           System.out.println(shapes[idx].computeArea());

Another option you might try is to delete the code in the method, as follows:

public double computeArea()
{
   return 0;
}

You have to return something because the method declares a return type. This will work just fine, but it still does not prevent another programmer, three years from now after you have moved on, from defining a new child class for a new shape and mistakenly not overriding computeArea with a concrete implementation. The programmer will not know that he or she is expected to override this method, and so will be dumbfounded when the first tests result in zero for the area.

What you really need, and what Java provides, is a way to indicate that this method is declared, but not defined, in the parent class. That is, Java gives you a way to define the exact signature of the method (but not the body) and have the compiler force child classes to implement that method. This is achieved in Java through the use of the abstract modifier. This modifier identifies to the compiler that this method must be overridden and concretely implemented in each class that extends this parent class. The computeArea method will then look like this:

public abstract double computeArea();

Note of the semicolon at the end of the signature, versus the typical braces and method body. There is no need to define an empty body. This method has no meaning in this parent class and is, in essence, merely a placeholder for child classes to indicate that they must implement this method. This very nicely solves the problem! You can now write generic code that appears to call the method computeArea in the parent class, and be guaranteed it will be implemented by all child classes.

Here is the compile error you get if you write a parent class that does not implement the method:

D:\bookj4c\samplesrc\ch9>javac Circle2.java
Circle2.java:4: class Circle2 must be declared abstract. It does
not define double computeArea() from class Shape.

Thus, you see that the compiler forces child classes to implement this method.

Abstract Classes

The use of abstract methods in a class informs the compiler that any class extending this class must override and implement this method. It also informs the compiler that this parent class cannot be instantiated directly using new. Rather, only child classes that extend it and implement the abstract method(s) may be instantiated. Why? Because if you were allowed to instantiate this class as an object, and you subsequently invoked the abstract method computeArea, you would encounter a runtime error. When you define a method as abstract, you are also forced by Java's rules to specify the abstract modifier at the class level, as shown in Listing 9.12.

Listing 9.12: Classes with even one abstract method must be declared abstract at the class level.
Start example
/**
* Abstract Base Class for two dimensional shapes
*/
public abstract class Shape
{
      protected double height=0;
      protected double width =0;
   protected double radius=0;

   /**
   * Compute the area of the shape. Child classes must override.
   */
   public abstract double computeArea();
} // end class Shape
End example

This informs potential users of this class that it is an abstract class because it has one or more abstract methods, and so cannot be used directly. Any attempt to instantiate such a class will result in a compiler error, as follows:

D:\bookj4c\samplesrc\ch9>javac TestShape2.java
TestShape2.java:5: class Shape2 is an abstract class. It can't be
instantiated.
        Shape2 shape = new Shape2();
                       ^
1 error
Errors found in compile.

Tools such as VisualAge for Java also pick up on this modifier and display the class name in their IDE with a different font or in superscript to render it visually obvious. This also alerts potential programmers who want to extend this class that there are methods in the class that must be overridden and concretely defined.

Note

Unlike the final modifier, specifying abstract for a class does not make or require all methods to be abstract. Rather, it implies only that some of the methods are abstract.

You can have more than one abstract class in a hierarchy. You might decide to extend one abstract class with another abstract class, in order to further refine the abstraction. For example, you might decide to have an abstract child class of shape named RectangularShape for four-sided shapes, and define both Square and Rectangle classes to extend it. This is fine, and quite common. As long as you specify the abstract modifier for the new child class as well, you will not be forced to implement the abstract methods in it, although you are permitted to do so. Eventually, some child class must be defined without abstract.

Note

Classes and methods that are not abstract are said to be concrete.

Constructors and Inheritance

As you recall, classes can have constructors. In fact, they usually do. These constructors are simply special methods with the same name as the class, and without any return type specified. There can be multiple constructors, each taking different numbers or types of parameters. You do not call these constructors explicitly; rather, the Java runtime does this implicitly when you instantiate an object using the new operator. If you have multiple constructors, Java calls the one that exactly matches the number and type of parameters specified on the new call.

When you have a class that extends another class, it is your responsibility to invoke the appropriate constructor for the parent class. Suppose the Education class extends the BillableService class. Then, suppose that BillableService has a constructor that takes in the number of hours to be billed, the customer name, and the names of the employees who provided the service, as shown in Listing 9.13.

Listing 9.13: A base class, and its constructor, for tracking billable services.
Start example
class BillableService
{
   // …instance variables
   // Constructor:
   public BillableService(int numberHours,
                        String customerName,
                        String[] employees)
   {
      // …do initialization using passed in values
   }
}
End example

Now, consider Education, which is a child class of BillableService. It has a constructor that also takes in a course number, which is stored in a new instance variable in Education. This constructor must do the following, as shown in Listing 9.14:

  • Call the constructor of the parent class, with the required parameters.

  • Do its own initialization, as it deems necessary.

Listing 9.14: A child class whose constructor takes more parameters than its parent class.
Start example
class Education extends BillableService
{
   protected int course;
   // Course number instance variable
   // Constructor:
   public Education(int numberHours,
                  String customerName,
                  String[] employees,
   int courseNumber)
   {
      super(numberHours, customerName, employees);
      course = courseNumber;
   }
}
End example

Notice the use of the Java keyword super to invoke the parent class's constructor. This is, not surprisingly, the same keyword you have already seen for invoking other methods in a parent's class. When invoking a parent constructor from a child constructor, the following apply:

  • The syntax is super(parms), versus super.method(parms) for regular method calls.

  • The super call must be the first line in the child's constructor.

If you neglect to make this call to the parent's constructor, Java will automatically call the parent's default constructor for you—that is, the constructor that takes no parameters. This might result in incorrect initialization.

Multiple Inheritance

Other object-oriented languages, such as C++, allow you to extend not only one class, but multiple classes. This can be useful in many situations where you want a class to inherit the behavior (methods) of more than one existing class. For example, suppose you wanted to create a user-interface window to display employee information. You might want to define the window's class as extending both the Window and Employee classes, so your new class inherits the methods from both.

The Java language, however, does not allow this. The reason relates back to the Java designers' goal of keeping the language simple. Multiple inheritance, which occurs when a class extends more than one parent class, has an inherent complexity: if a method or variable with exactly the same signature exists in both parent classes, which one is used in the child class? This collision problem leads to some arbitrary rules that, in other languages, make the concept of inheritance more complicated—too complicated for the Java mantra of "keep it simple."

If Java did support multiple inheritance, it would probably do so syntactically by allowing multiple class names after the extends keyword on a class definition, separated by commas:

public class EmployeeGUI extends Window, Employee

This would mean that the new class would inherit all the methods and variables from both classes, in this case, Window and Employee. This class would have, then, two parents. Any number could be allowed.

Interfaces

While Java does not allow multiple inheritance, it does not disregard the subject totally. It is too important when designing object-oriented systems. Let's review the problem with it, though. The collision problems stem from multiple choices for identically named variables and methods existing in more than one parent class. For example, what if both Window and Employee had a method named toString? Which one does the child Employee-GUI inherit? That is, when you instantiate EmployeeGUI and call toString, which version is called? Similarly, if both parent classes defined a variable named, say, id, which one would the child class inherit? If you modify that variable in the EmployeeGUI method code, which one are you changing? What if id in one parent is defined as an integer, and in the other it is defined as a string?

Java defines a special, simplified version of multiple inheritance that uses a new term, interface. Java allows you to define a special type of class—an interface—that does not have any variables. It has only method signatures, not method bodies (actual code, that is). Let's look at an example, in Listing 9.15.

Listing 9.15: An example of a Java interface.
Start example
public interface Printable
{
   public void print();
}
End example

Note the use of the keyword interface rather than class. Interfaces are a specialized form of a completely abstract class (every method is abstract). Also, you don't have to specify the modifier abstract for the methods, as it is implicitly specified for you. In essence, interfaces are empty shells:

  • They have no variables. (They do, however, allow constants.)

  • They have no constructors.

  • They have no static methods (surprisingly).

  • They allow only method declarations (signatures), not method code.

  • They cannot be instantiated, that is, new is not valid for them.

How do you use them? The idea is that the class that wants the functionality "promised" by the interface (via its method signatures) defines in its class definition that it will implement that interface:

class PrintableEmployee implements Printable
{
   ...
}

By using the Java keyword implements, you are declaring that your class will implement each of the methods declared in that interface. Your class must actually define all the methods of the interface. If not, your compile will fail, as in the following:

D:\bookj4c\samplesrc\ch9>javac PrintableEmployee.java
PrintableEmployee.java:4: class PrintableEmployee must be declared
abstract. It does not define void print() from interface Printable.

This error message is interesting. It implies that a Java interface is a contract. When you declare that you will implement an interface in your class, you are agreeing to fulfill that contract by implementing all of the methods in that interface, using exactly the same signature. For example, you would have to add a method such as the one shown in Listing 9.16 to the PrintableEmployee class.

Listing 9.16: An implementation of the print method signature from the Printable interface.
Start example
/**
* Method print, as required by Printable interface
*/
public void print()
{
   System.out.println("Confidential");
   System.out.println("Employee ID, dept, name: "+id+","+dept+","+ name);
   System.out.println("Started on.......: " + startDate.getTime());
   System.out.println("Current salary...: " + salary);
   System.out.println("Current level....: " + level);
}
End example

Another way to look at it is that implements is similar to extends, but you are forced to override each and every method in the parent class (the interface class, in this case). This, then, is how Java permits a limited but still very powerful form of multiple inheritance—while a class can only extend one parent class, it can implement one or more interfaces, as in the following:

class MyClass extends YourClass implements Printable,
AnotherInterface
{
   ...
}

Interfaces have the same naming convention as classes: the first letter of each word is uppercased. Often, interfaces are named to end in "-able" to indicate classes that implement them are capable of performing the function implied by the interface.

A class that implements an interface allows you to call the same method in objects of many class types, even if those classes are not related to each other via the extends keyword. That is, you can write generic code that calls methods on objects, without declaring what class type those objects are. This way, the generic code can accept objects of any type, giving great reusability. This is the benefit of polymorphism, but without the requirement of using only classes that extend a common parent. Instead, you have the lesser requirement of only using classes that implement an interface. Instead of declaring an object type to be a parent class, you can declare an object type to be an interface, and the compiler will let you call any of the methods identified in that interface. You know that the object, no matter what its class type actually is at runtime, will have these methods because the interface contract guarantees it.

For example, consider the following generic (reusable) method that calls the print method on a given array of objects:

public void printAll(Printable arrayOfObjects[])
{
   for (int idx = 0; idx < arrayOfObjects.length; idx++)
         arrayOfObjects[idx].print();
}

Notice the declared type of the array parameter arrayOfObjects. Instead of specifying a parent class, like Shape, it specifies an interface name, in this case Printable. Recall that whenever a variable or parameter type is defined to be a class name, you can actually assign it or pass it an object of any class that extends that declared class. Well, when you declare a variable or parameter type to be an interface name, you can actually assign it or pass it an object of any class that implements that declared interface.

Interfaces allow you to write generic (reusable) code like the printAll method, and call methods on objects of classes that you know nothing about. Recall, from the discussion of polymorphism, how you had to define the method that a generic code called, in the parent class. You had to use the parent class name (Shape) in the generic code method parameter (printAreas in Listing 9.11). This implies that all runtime objects to be used in the array that is passed to printAreas must be objects of children classes of Shape. Interfaces simply allow this same polymorphic behavior, but they remove the restriction that only child classes of some parent class can participate.

Consider the Shapes example. What if a tool's palette also had icons for creating various types of text strings, instead of just geometric shapes? You certainly wouldn't want these classes inheriting from the parent Shape class. Many of Shape's methods wouldn't apply to text strings. Rather, they would have their own parent class called, say, Text, with methods common just to text strings, such as setFont. However, the tool framework will still need to do common actions on both Shapes and Text, such as draw or possibly even computeArea (for color-fill action). The action is common, but the implementation details will be different between classes that extend Shape and those that extend Text.

We would simply create an interface, called, say, PalettePart, and put in it signatures for the actions the tool will expect to carry out on any objects that come from the palette. This also makes it possible to put any new items on the palette. As long their class implements this interface, they will work with the tool.

Typically, interfaces are quite small, with only a few method signatures. Often, there is only one signature. They are typically used to "mix in" specific, well-defined functions. Interfaces are used to circumvent the single-inheritance rule in Java. In reality, they are just purely abstract classes that have no instance variables and only abstract methods.

Why Interfaces?

We know that you are not convinced of the value of interfaces yet. You are probably having trouble understanding why and when to use them. "What is the point of methods that have no code?," you might ask. Don't worry. You don't need to define your own interfaces for some time yet, anyway. You only need to define your own interfaces when you are writing truly reusable code, and you cannot predict the names or hierarchies of the classes that will be used.

Interfaces are usually used by software vendors writing code that can be leveraged or used by others who purchase the software. To help you see the value of interfaces, consider how you would write a payroll system without them, at a high level. Imagine you were writing the payroll application for your company, and you knew your code uses the parent class Employee and child classes of it (such as the SalesClerk class from an earlier example in this chapter).

You might end up writing many thousands of lines of code to implement such a complex system, and throughout that code would be numerous references to your Employee class, calling many of the methods in the class. By only using a parent class name (like Employee) throughout your system, you have done yourself a favor for future expansion. You can add new employee types such as Manager, Contractor, PartTime, and so on, by simply creating these new classes as child classes of Employee and overriding the methods you want to uniquely implement. Your thousands of lines of payroll processing remains completely unchanged! This is a very nice design.

Now consider that you want to sell this payroll application. How can another company use it unless they happen to have called their employee class "Employee" as well, and written exactly the methods your code wants to call? If you use an interface instead, anyone can adapt to this system by simply changing whatever their employee class happens to be, to implement the "Payable" (say) interface. They would then implement each of the methods that your code wants to call. They might do this by simply calling their own existing methods that do the same function. With this change, they can now use your entire payroll application without changing it at all.

Think about this—it allows vendors to write code that you can adapt by changing your own code, not the vendor's code. That saves all the headaches you experience when you have to merge your changes to vendor code with the vendor's next release. You could also put this flexibility to use even in-house, if your company purchases another company, and your job is to merge the payroll systems. You simply change the other company's system to implement your interface, and your system will now be leveraging theirs.

How would you change your system to use interfaces instead of the Employee parent class? Just like you did in earlier examples. You would define an interface that contains signatures of all the important methods in your Employee class, then change your Employee class to state that it implements that interface. Now, the important part: you would change all the places in your payroll system that used Employee as a class type (for variable declarations or parameter definitions) to instead use the interface name. You would now be set to allow any class to be used in place of Employee, as long as it implemented that new interface.

Got it? Well, not to worry, what is important for now is only that you understand how to use interfaces, because they are used frequently inside the Java packages that make up the Java Development Kit. And using them is easy; you simply add implements XXXX to your class definition, and then implement the methods that are defined in the interface. Nonetheless, we continue in the next few pages to help you understand why interfaces are important.

Interface Are Callbacks

Interfaces are really just a way to write callbacks in Java. That is, by implementing those methods, other code in other classes can call those methods in your class. They can "call back" to you.

Callbacks are a programming idiom, not a defined language concept. Their purpose, in all languages, is to allow you to write generic code that can be easily used by anyone. Callbacks in other languages usually invoke a utility function that requires the caller to provide an address of a function that the utility function will use to call back to the caller. That is, rather than hard-coding some algorithm that will not work in all situations, you take that algorithm out of your code and put it in a separate function. Then, you ask anyone who calls it to pass in either the address of that function (so you can call it) or the address of another function of their own choosing, if they wish to substitute their own algorithm.

In Java, you would do this by defining your code to take an interface instead of a function pointer. Then anyone could call our code and pass in an instance of a class that implements that interface. Our code will then call the method it needs in that interface—it will call back to your code. Even though our code has never met your class, we are guaranteed that your class has the methods we need to call because it implemented the interface that defined those method signatures. We cannot be guaranteed that you coded those methods correctly, but at least we know you coded them.

Let's use an example to make this real. The alternative to using callbacks and thus interfaces in many situations is to try and predict all the possible variations you might need, and supply overloaded methods to do each of them. Consider the method from chapter 6 for searching a given integer array for a given integer, as shown again in Listing 9.17.

Listing 9.17: A method for searching for an integer value in an integer array.
Start example
public static int searchArray(int searchArray[],
                              int searchArgument)
{
   int matchIndex = -1;
   for (int idx=0; idx<searchArray.length && matchIndex < 0; idx++)
      if (searchArray[idx] == searchArgument)
         matchIndex = idx;
   return matchIndex;
} // end of SearchArray method
End example

This method takes an array of integers, and an integer search argument, and loops through the array until it finds the argument. If the argument is found, it returns the array index position of the match, otherwise, it returns –1. This is nice for arrays of integers, but what about arrays of objects? For example, suppose you wanted to search an array of Shape objects for a given shape. You define two shapes as being the same if they both cover the same area say (unrealistic, perhaps, but it will suffice for this example). So, you want to search an array of shapes until you find an element that is the same (by this weak definition) as the search argument. Listing 9.18 shows what the overloaded version of this method will look like for Shape objects.

Listing 9.18: A method for searching for a shape value in a shape array.
Start example
public static int searchArray(Shape searchArray[],
                              Shape searchArgument)
{
   int matchIndex = -1;
   for (int idx=0; idx<searchArray.length && matchIndex < 0; idx++)
      if (searchArray[idx].computeArea() ==
         searchArgument.computeArea()) matchIndex = idx;
   return matchIndex; }
// end of SearchArray method
End example

This method is identical to the first, except that it is defined to accept a Shape array and a Shape search value, instead of an integer array and an integer search value. The comparison to determine a match is different, too. You can't just compare memory addresses (like the equality operator does), but rather call the method on each object that will return the attribute you decide to do the match testing on.

Now imagine that you like this so much, that you want a similar function for arrays of Employee objects. You want to search an array of Employee objects until you find an element who has the same ID as the Employee object search argument. Listing 9.19 shows what this version would look like. This version again changes the parameter types and the code to compare the two objects: the array element and the search argument.

Listing 9.19: A method for searching for an employee value in an employee array.
Start example
public static int searchArray(Employee searchArray[],
                              Employee searchArgument)
{
   int matchIndex = -1;
   for (int idx=0; idx<searchArray.length && matchIndex < 0; idx++)
      if (searchArray[idx].getID() == searchArgument.getID())
         matchIndex = idx;
   return matchIndex;
} // end of SearchArray method
End example

So now you have yet another very similar, but different, version of the generic helper utility. Tomorrow, if you decided to search an array of MyClass objects for a match on something, you would have to write yet another version of this. It gets kind of crazy—you want to write generic utility methods to save code, but you end up repeating code and endlessly writing new variations.

Interfaces can help. Let's use an interface to write a truly generic search utility that can be called to do a search on any array. To do this, we need to abstract-out the variable parts of the search and put them in their own method. We will not write the code for this method, but rather leave it up to our callers to supply, so we don't get into the game of writing every possible variation of it.

The variable part of our search compares the search argument object to the array element object. So, let's depend on the objects themselves knowing how to do this comparison. Assume that all objects we are interested in supply a method called sameAs, which takes a second object of the same type as a parameter and returns true if it decides the two objects are the same, and false otherwise. You can abstract this thought into an interface called Searchable, as shown here:

public interface Searchable
{
   public boolean sameAs(Searchable argument);
}

Now, let's use this interface to write a truly generic array-search method, as shown in Listing 9.20.

Listing 9.20: A generic array-search method.
Start example
public static int searchArray(Searchable searchArray[],
                              Searchable searchArgument)
{
   int matchIndex = -1;
   for (int idx=0; idx<searchArray.length && matchIndex < 0; idx++)
      if (searchArray[idx].sameAs(searchArgument))
         matchIndex = idx;
   return matchIndex;
} // end of SearchArray method
End example

You can use this method to search any array of objects for a match. Well, not just any array, actually. The array has to hold objects whose class implements this interface. But, for your own classes at least, this is very easy to do—you just have to code a single, simple method (sameAs). For each class that you do this for, you get to make your own decisions about what it means for two objects of that class to be the same. For shapes, it might be something reasonable like identical length, width, and radius values. For employees, it might be identical employee IDs.

Java supplies just such an interface, but calls it Comparable (in the java.lang package) and calls the method inside it compareTo. Instead of returning true or false, it returns -1, 0, or 1, identifying the comparison object as less than, equal to, or greater than the target object. Many of the classes that Java supplies implement this interface already. This is how Java supports its generic array methods like binarySearch and sort, as described in chapter 6.

Interface Summary

It's time to summarize, formally, the new Java language construct you have just explored. An interface definition is a reference type definition, as is a class. This is in comparison to primitive data types, like an integer. An interface exhibits much of the same behavior as an abstract class that contains only abstract methods and constant instance variables

(public, static, and final).

Here are some things to keep in mind when defining interfaces:

  • Like classes, interfaces have package rights by default, but can be assigned public. At this outer level, public versus package affects initial access rights to the type, rather than the individual members (methods and variables). So, if you do not specify public, only classes within this package can implement this interface.

  • Like classes, interfaces can inherit from one another by using the extends keyword. Unlike classes, you can inherit from more than one interface at a time, separating the interface names with commas.

  • Interface names must be unique within their scope, both for classes and interfaces. That is, you cannot give the same name to both a class and an interface within the same package.

  • An interface body can consist of variables and methods.

  • All methods in an interface are implicitly public and abstract, although these keywords can be redundantly specified. No other modifiers are permitted for interface methods.

  • Interface methods, since they are abstract, have no body, just an ending semicolon.

  • All variables in an interface are implicitly public, static, and final, although these keywords can be redundantly specified. No other modifiers are permitted for interface variables.

  • All variables must be explicitly initialized as part of their definition (since they are constants, after all).

Interfaces, then, allow you to inherit method signatures (via the implements keyword in a class definition). But, if this is all you get, instead of the actual code for the methods, why bother? Why not simply implement the methods directly in all the classes that need them, without bothering with the implements keyword and the interface at all? In the end, it seems, there has been no code sharing—the intended purpose of inheritance all along.

This is a key question, and something with which you should become comfortable. The answers are as follows:

  • An interface is merely a tag for the compiler. It is a mechanism to inform the compiler that, yes, this class (which implements the interface) really does support the methods named in the interface. This allows you to write generic utility code that works on objects of any type, as long as they implement that interface. If you do not need such generic utility code, you do not need an interface.

  • Multiple inheritance, in the end, is not that useful for fully implemented methods (versus abstract, signature-only methods). If a set of fully implemented methods is generic enough to be "mixed in" at will with any class, then clearly it is independent of the class in the first place, and inheritance is not necessary. In this case, you might as well just declare an instance of the separate class inside your class and call its methods when you need to. This is why it is not a big loss that, compared to true multiple inheritance, interface methods have no body or implementation. Presumably, if you want to inherit these methods into many of your classes, you want to supply unique implementations anyway. If you want to inherit them into only one class hierarchy—in other words, simply define the methods in your base (parent) class—you do not need multiple inheritance.

Have you got it yet? Do not be concerned if you do not; it is a tough concept that will come with time. Some examples will help, supplied in the following sections.

Java-Supplied Interface Examples

The Java language is riddled with interfaces in its supplied packages. We cover but a few here, not only to give you a feel for interfaces, but also to expose you to these useful interfaces that you should know about anyway.

The Cloneable Interface

One example of interfaces you should be aware is the Cloneable interface. This interface is defined in the java.lang package, which is the one package that is automatically imported for you. Cloneable is an interesting case—it is a completely empty interface! It highlights the fact that interfaces are really tags for the compiler. The java.lang package class called Object (from which, as you now know, all classes inherit) has a method in it called clone. The purpose of this base method is to create a new instance of a given object that is identical to the current instance:

ClassA objA = new ClassA();
ClassA objB = objA.clone();

It is important to have this method. Otherwise, you might be tempted to use this:

ClassA objB = objA;

This does not copy objA at all! It simply creates a second reference to it. (Object variables, again, are merely references to allocated objects. That is, they hold object addresses, or pointers.) In order to create a second copy of an object, it is up to the object itself to supply a method. This is what clone does. It essentially instantiates a new instance of this class (using the default constructor only), and copies bit-by-bit all the instance variables in the current class to the new class.

Sound straightforward? Well, it is not! The problem is that you may have objects as instance variables, and as we just discussed, copying an object variable only produces a second reference to the same object, not a second copy of that object, as you would expect in this case. That is, nested objects need to be cloned, too. This job is not something that the base clone method inside Object can take on by itself. It needs to be carefully implemented by each class—by you. So, Java makes the clone method in Object protected. It can only be called by you in your class. Thus, objB = objA.clone(); will always fail with a "method not accessible" error message unless you do the following:

  • Override and implement the clone method in your class.

  • In that override, change the access to public.

And that's not all. The language designers were so anxious that clone only be supported by classes that really think they have implemented it properly, that they force you to also tell the compiler explicitly that you support this method. How? By defining your class as implements Clonable. Only if all this has been done can a user invoke the clone method on a class.

If you do decide to support this useful function, your clone method would first invoke your parent's clone method with super.clone();, then invoke the clone method on all of your object instance variables (if they are not null), and finally copy all the primitive data type instance variables.

This is a case where an interface is used in a unique way, as a tag to identify this class's support for a particular function. Can you do this, too? Yes, you can. There is a way to determine programmatically if the class type of a given object implements a particular interface—by first using the Object method getClass to return the object's Class object, and then calling getInterfaces on the Class object. This returns an array of Class objects, one for each interface implemented by this class. You can then loop through this array, invoking getName on each entry to look for the interface name you are interested in.

This discussion about copying objects applies to testing for equality, too. You cannot test if two objects are equal like this:

if (objA == objB) then ...

Since object variables are really addresses of allocated objects, this test will only test if the two variables refer to the same place in memory. It is really up to each class to determine equality. You might decide, for example, that two instances of your class are equal if they have the same values for some key instance variables. The Java convention for this is to supply a method called equals:

public boolean equals(MyClass anotherObject)
{
   ...
}

A useless version of this is supplied in Object, so you are overriding it by supplying your own. In this case, though, no "implements xxx" tag is required in your class.

The Enumeration Interface

In the Java utility package called java.util, there is an interface called Enumeration. This is a convention that classes should use when they want to support the idea of enumerating, or walking through a list of items.

Remember the Employee class, earlier in this chapter? Imagine you also have a Department class that contains an array of all employees in the department, and some other useful department-level information. This Department class might be useful in an organizational-chart program or any other program requiring information by department. Listing 9.21 shows what it might look like.

Listing 9.21: Using an interface called Enumeration to walk through an array.
Start example
public class Department
{
   private int      deptNbr;
   private String   deptName;
   private Employee deptMgr;
   private Employee deptEmployees[]; // defer creation until we know # employees

   public Department(int deptNbr) // Constructor
   {
      this.deptNbr = deptNbr;
      // NOT SHOWN: read department information from database,
      // populate instance variables, including deptEmployees, from database
   }
   public int getDeptNbr()
   {
      return deptNbr;
   }
   public String getDeptName()
   {
      return deptName;
   }
   public Employee getDeptMgr()
   {
      return deptMgr;
   }
   Employee[] getEmployees() // package access only
   {
      return deptEmployees;
   }
} // end class Department
End example

Notice the array of employees. It would be nice to allow other code to get each employee from that list, one at a time. Allowing other code to iterate through a list is a common programming requirement, and it makes everyone's life easier if you do this consistently whenever you need to. All Java classes that contain collections of items support this type of "walk the list" functionality, such as Hashtable in the java.util package. They do it consistently by implementing an interface designed just for this, an interface called

Enumeration.

Here is how it works. Enumeration contains two method signatures:

  • hasMoreElements returns true if there are more elements in the collection.

  • nextElement returns the next element in the collection.

The intended usage for these is as follows:

while (obj.hasMoreElements())
   nextObj = obj.nextElement();

Obviously, for you to implement these methods in your class, you need to record some state information, namely, the current position. For this reason, you don't usually implement this interface directly in your class; rather, the preference is that you supply an entirely new class that implements them. This allows users to have more than one enumeration active at once, each with its own instance of this separate class.

This new class must take an instance of your class in the constructor, then initialize and maintain a position variable to support the implementation of the two methods. The Department example's enumerate class might look like Listing 9.22.

Listing 9.22: An example of an enumerate class called Our Department.
Start example
import java.util.*;

class EnumerateDepartment implements Enumeration
{
   private Employee employees[];
   private int pos=0;

   // constructor
   EnumerateDepartment(Department dept)
   {
      this.employees = dept.getEmployees();
   }
   // end-of-list test...
   public boolean hasMoreElements()
   {
      return (pos < employees.length);
   }
      // retrieve next element...
      public Object nextElement()
   {
      Object retobj = null;
      if (hasMoreElements())
         {
            retobj = employees[pos];
            pos = pos+1;
         }
      return retobj;
   }
} // end EnumerateDepartment class
End example

Now, you need to supply a nice little method in your Department class that will return one of these (be sure to import java.util for the Enumeration interface):

public Enumeration getEnumeration()
{
   return new EnumerateDepartment(this);
}

Finally, users who want to enumerate the entries in an instance of Department will have code that looks like this (again, be sure to import java.util for the Enumeration interface):

Enumeration enum = aDepartment.getEnumeration();
Department  nextDept;
while (enum.hasMoreElements())
    nextDept = enum.nextElement();

You might be wondering why you need or want to use Enumeration in this example, since the Department class has a method called getEmployees that returns the array of employees anyway. There are two very good reasons:

  1. As programmers get used to enumerations from the Java-supplied classes, they will expect it from your classes. It reduces their learning curve by reducing how much "discovery" they have to do in order to use your classes.

  2. You might decide in the future to use something other than an array to store the Employee objects in the Department class. Perhaps you decide a vector is better, or one of the new JDK 1.2 collection classes. Using an Enumeration interface shields your users from knowing or caring about such implementation details. You can make this change at any point without affecting users of this class, but only if you've hidden the fact that you use an array behind a curtain such as Enumeration provides. That is why we made the getEmployees method package access instead of public access. We really only want the Enumerate-Department class to use it.

You have seen two examples where Java makes good use of interfaces, but there are many more. For example, chapter 12 shows that Java uses interfaces to "call you back" when an event happens, such as a user pressing a button. Your GUI class implements the appropriate interface, and this allows Java to be sure you have implemented the explicit method it will call when this event happens. Java does not know anything about your classes, so how else could it call a specifically named method in them? Now, it's your turn to invent some interfaces!


O2O-Object To Object: Casting Objects

 Team-Fly

O2O—Object To Object: Casting Objects

You already know that in Java, as in COBOL and all languages, you can cast variables of basic data types, such as integer and float, from one type to another. In Java, you can cast a data type up to a higher precision implicitly, as in the following:

int a = 10;
long b = a; // cast integer (4 bytes) to long (8 bytes)

If you want to cast down to a lower precision, you must do so explicitly, using the (type) notation, as in the following:

long b = 10;
int a = (int)b; // cast long (8 bytes) to integer (4 bytes)

What about object variables? Can you cast an object reference of one class type to an object reference of another class type? Would you want to? The answer, to both questions, is yes. Like casting of primitive data types, though, it has it rules.

Why Cast?

Recall the discussion of polymorphism and interfaces, and how utility methods will often describe a parameter as a parent or interface type, yet accept object parameters of children classes at runtime. The actual parameter passed may be of a type lower in the hierarchy tree than was declared for the parameter type (again, think of interfaces as purely abstract parent classes). It is also common to define an array of parent type classes and populating it with actual objects from lower in the hierarchy tree. Both cases allow common code to invoke parent-defined methods on the actual objects, and have the appropriate versions of those methods invoked, polymorphically.

What happens if there is a special case where you really need, in this common code, to invoke a method that is only defined in the child class? At compile time, such an attempt will fail because the compiler looks in the compile-time parent class for the method. If the method does not exist there, you will not be able to compile this code.

You should try to avoid this situation by defining the needed method in the parent class as an empty, do-nothing method, if necessary. There are still times when you will feel forced to do this, however. This requires an explicit cast on your part to force the compiler to treat the object as a new type.

Think back to the Employee class example, and its child class, SalesClerk. Recall that SalesClerk had a unique method, getCommissionRate, that its parent Employee did not have. This was fine, because the generic doPayroll method that worked on an array of Employee objects did not try to call this method. But what if you wanted it to? By the rules of polymorphism, generic code that is declared to work on a parent class can only invoke methods defined in the parent class. It does not matter that at runtime you might actually have objects of child classes, the compiler only looks at the methods in the declared parent class.

If you really, really want to call a method that is unique to a child class, you must first cast a runtime object to an object reference variable declared to be of the child class type. Then, you are legally allowed to call all the methods in that child class. That cast is done syntactically the same way you cast primitives, for example:

SalesClerk sc = (SalesClerk)employees[idx];

This casts the element in the Employee array to a SalesClerk object. Unlike primitive type casting, this will fail if the runtime type is not actually of an object of the target type.

Thus, you really need to test first if that is the case, since the array will probably hold both Employee objects and SalesClerk objects at runtime. Here is how you do that:

SalesClerk sc;
if (employees[idx] instanceof SalesClerk)
{
   sc = (SalesClerk)employees[idx];
   // call unique methods in SalesClerk class
}

The instanceof operator is a binary operator. It requires an object reference variable on the left, and a class name (without quotes) on the right. It returns true at runtime if the object is an instance of the class. Otherwise, it returns false. There is no polymorphic behavior here; the object must be of that class type to get true as a result. The instanceof operator is only used in expressions, since it has no side effects.

Again, you should try to avoid using object casting and the instanceof operator, but occasionally it is unavoidable.

The Rules of Object Casting

Here are some rules for casting objects:

  • You can only cast between objects in the same hierarchy tree.

  • If you cast from a parent class type to a child class type, an explicit cast is required.

  • If you cast from a child class type to a parent class type, no explicit cast is required.

  • If the actual object at runtime is not of the target type, you will get a runtime error. That is why you should always use the instanceof operator to verify this.


Common Methods To Override

You know now that all the classes you create in Java implicitly extend the Object class. This gives users of your class access to all the non-final, non-abstract, non-private methods in this universal class.

However, there are a few methods, shown in Table 9.2, that you should consider overriding in your new class every time. This is not to say that every class you create needs to support all of these methods, but you really should make an explicit decision in every class whether to implement these methods or not.

Table 9.2: Methods to Be Overridden.
Method | Description
finalize | This method is called by Java when an object of your class is swept up by the garbage collector. If you have any finalization or cleanup code to be done when the object is destroyed, such as closing any possibly still open files, put it here.
clone | You have seen this already; it is a convention to supply this method if you want to enable object duplication.
equals | By convention, users will invoke this method to compare two objects of your class. If you want to support the idea of equality testing, you must supply this method, since the default implementation returns false unless you are comparing an object to itself.
hashCode | This returns a hash code, or unique key, derived from this object. If you want to store objects of your class in a hashtable object, you need to implement this and return the hashCode of your key field.
toString | This method is called whenever someone tries to convert an object of your class to a string, such as by specifying an object name in the System.out.println method. This should return a string with something useful in it.


The OO Way Of Life

At this point, you have seen all the language tools and terminology for object orientation. So, are you ready to design your first object-oriented project? Probably not. Using these tools efficiently is a skill that comes with time. The process of designing a good object-oriented system is different than the procedural systems that you are used to. It involve a somewhat new way of thinking—thinking in terms of objects.

Consider how you design or develop now with COBOL. Everyone is different, but chances are your up-front design centers first around the functionality (subroutines, procedures, and calculation specifications) that you will need to solve the problem. The data (data structures, arrays, and so on) that you will need to support this functionality comes in at a secondary stage. We are not talking about the database design here, although, for many, that design emerges from this function-centered process.

With OO, the design process is reversed. Rather than first thinking about the functions required to get the job done, you are required to first consider all the objects needed. And what are objects, again? Instances of classes. And what are classes again? Data (variables) and supporting functions (methods). You need to start thinking of data and functions as a single unit in order to grasp OO. Then, you can start thinking about how to partition your "problem domain" into such units (objects).

Some programmers who lack actual experience writing OO have difficulty climbing this conceptual hill. You might be surprised at some of the things that become objects in an OO world. For example, who would expect a color, like red, to be an object? Who would expect a two-dimensional point (with x and y coordinates) to be an object? In many systems, like Java, they are. Because executable code in Java can only exist inside objects, everything is an object. Well, to be precise, everything is either an object or a simple variable inside an object.

In Search of Objects

There is no magic formula to help you begin thinking in terms of OO. There are, however, many books that describe the up-front part of an OO project—the analysis and design (OOA, OOD). You will want to read one or two. All of these books discuss at least the following:

  • How to find objects in your "problem domain."

  • How to find and define the roles of your objects (that is, the methods).

  • How to find and define the relationships among your objects (that is, who uses what).

  • How to consistently document your design using a language for writing down your object-oriented design, with conventions for depicting classes, objects, relationships, and so on.

The collective approach that a book uses to describe all of this is known as its methodology. Such methodologies are usually named after the person or persons who have defined them. Is there a "best" methodology? Certainly. It is the one that you decide to use. Pick one, roll up your sleeves, and dig in. (Or hire experienced OOA/D programmers, and go with their recommendation.)

One methodology of which you should be aware is UML, or Unified Modeling Language. It is a convergence of a number of leading methodologies, and has become the industry standard. It was defined by the highly regarded methodologists Grady Booch, Ivar Jacob-son, and Jim Rumbaugh at Rational Software, the makers of the immensely popular OOA/D tool Rational Rose. For more information on UML, see Rational's Internet Web site at www.rational.com/uml.

UML, and other methodologies, define not only ways of attacking the decomposition of a programming assignment into the OO code to implement that assignment, but also the graphical notation for describing your OO design. For example, recall how we showed classes as boxes, and their child/parent relationship as an array pointing up from the child to the parent class. That is a UML standard. There are other such graphical standards for depicting classes versus objects, and what classes use other classes by containing an instance of them as an instance variable or by accepting objects of them as parameters in methods. Many OO design tools, such as Rational Rose, support this graphical standard and will produce it for you. You use the tool's palette to drop classes and objects, and then use it to make the appropriate connections.

In an OO project, you spend much more time at the beginning, thinking about the problem to be solved, than you do in the later stages. This is a good thing, because you know from experience that the more time you spend in the front end of the project, the less time you have to spend in the back end (coding and testing). The objectives of OO are to reduce maintenance efforts and substantially increase code reuse. By spending time analyzing the problem domain in the search for objects, you typically improve your final time-to-market and ongoing quality, because we

  • Drive out a better problem statement (a crisp list of requirements).

  • Derive a system that better models the actual real-world problem, leading to end-user satisfaction and higher quality.

How do you find objects? The usual trick is to start with your requirements statement, and look for nouns. These are the "low-hanging fruit"—they almost always become objects. More specifically, they become classes. Later, you determine how many objects you might expect to make out of these classes. For example, consider the objects car, employee, manager, bank account, customer, and service. These are all examples of problem-domain objects. Your system will also require many implementation objects that will be needed, in the end, to do the job. These are helper classes with commonly needed functions like tax calculations, date manipulations, and string manipulations.

After some experience, you will get better at finding these objects up front, but initially you might have to stumble into them as you go. And remember, from your second project on, your first job is to look at the classes you have already created to see what can be reused. There's nothing like an existing, well-tested class to jump-start your new project!

How do you find roles? These affect what methods your objects will have, and the variables needed to support those methods. The usual trick is, again, to start with your requirements statement, and this time look for verbs. For example, you might expect to have methods to do printing, sorting, calculating, ordering, billing, and so on. Establishing which roles are played by which objects is also important at this early stage.

How do you find relationships? This requires you to address design issues like class hierarchies, object parameters, object instance variables, collections of objects, and so on. How objects will use each other, including quantitative metrics ("how many"), is important to consider at this early stage. This is where you typically have to make some tough decisions, which will be affected by such real-world things as performance.

In our experience, some of the more interesting decisions that programmers have to face are centered around inheritance. The trick is to know when to inherit from another class rather than do one of the following:

  • Declare an instance of the parent class as an instance variable in your class. This is known as containment because your class contains an instance of another class.

  • Pass in an instance of the parent class as a parameter to the methods that need it. This is known as usage because your class uses an instance of another class.

Again, there are general rules that cover inheritance. The idea in OO is to define objects that model, as closely as possible, the real world you are interested in. This means you rely on the real world to make decisions for you wherever you can.

In order to decide if a class should inherit, contain, or use another class, you should ask yourself some questions about the real-world relationships between the two classes:

  • Is the new class an example of the first class? This is an is a relationship, and if you answer yes, it implies that your new class should inherit from the first class. One classic example involves animals: if you have a class called Animal with lots of common roles like eating, sleeping, or writing code, then what would a dog be? A dog has unique implementations of these roles, so it should be its own class, but should it contain an Animal class object or inherit from one? The answer is that, in the real world, a dog is a type of animal, and therefore, the Dog class should inherit from the Animal class.

  • Does the new class have an example of the first class? This is the has a relationship, and if you answer yes, it implies that your new class should contain an instance variable of the first class. An example is a program for an automobile-parts business. Given a Chassis class, what should its relationship be to the Car class? Is a car a chassis? No. Does a car have a chassis? Yes. Clearly, then, the Car class should contain an instance variable of a Chassis class. Furthermore, in this example, it should contain exactly one such instance variable. (This is a quantitative metric.)

  • Does the new class use an example of the first class? This is the uses relationship, and if you answer yes, it implies that your new class should accept as a parameter an instance of the first class. This is typically a case where you are defining a helper or utility class that performs some commonly needed function on any given object. A classic example is an iterator. This is a class that has functions for iterating through a collection of objects. It uses the collection of objects to walk through it. Does a car have a driver? Well yes, but it isn't one driver for the life of the car, so it is more correct to say a car uses a driver.

Sometimes, the notion of has a versus uses is fuzzy. This is particularly true for utility classes. One decision you will have to make when defining such a class is whether the methods will all take objects as parameters, or whether the constructor will take an object and store away its reference in an instance variable upon which the methods will subsequently act. The answer is depends on the situation, of course, but it is not always clear, initially.

Passing an object in the constructor means that you have a case of containment, and you will need a unique instance of the class for each object on which you want it to act. You will usually do this when some state information about the object is required between method invocations (such as an iterator class that needs to remember "current location").

Passing the object in via a method is usually used when no state information is required between invocations, as in the doPayroll example earlier in this chapter. Sometimes, you will decide to support both for efficiency. You will allow the user to optionally pass in an object at constructor time, and to change or set the object dynamically via a setXXX method. In this case, you are using both containment and usage. The advantage is that your caller has to undergo the overhead of instantiating an object of your class only once. For high-use utility classes, this can be a significant consideration.

Reuse Considerations

When designing your classes, be sure to think about the next project. You want, as much as possible, to define classes and interfaces that are generally reusable. This means that you should try to abstract-out the general problem from the specific problem, and define parent classes and interfaces that can be reused in different hierarchies later on.

The trick is to get the abstracting right when you create class hierarchies. That is, you want to define the parent class so that it is reusable in new situations by simply creating new child classes. For example, rather than defining a totally unique class for each type of widget you sell, define a parent or base class called BaseWidget with common meth- ods all widgets need to support, like get/setPrice, get/setDescription, get/ setRelatedWidgets, and so on. For each type of widget you sell, create a child class that extends this parent class, and override the inherited methods as needed. All your utility-class methods will be defined to accept an object of this parent class as a parameter, and through polymorphism, call the real methods in the child classes. As you add new widget types later, these are simply new child classes. All your utility methods will still work as-is, even with objects of the new class.

Try to use interfaces. These are the ultimate in abstraction. For example, you might have a ZipCode class with one method, verify. This is fine for U.S. customers, but what if you expand into Canada, which has postal codes instead of zip codes? You will need to create a new PostalCode class, and rewrite the verify method. Then, your user interface code will have to figure out which class to use. This problem would happen again for every new country you enter.

Since the verify method implementation is totally unique per country, you should create an interface called Zip with one method signature, verify. Then, implement that interface and method in each of your classes, like ZipCode and PostalCode. Thus, your user interface code remains the same, it simply defines a variable of type Zip, and equates it to an instance of the appropriate class. It can then call the verify method on that object, no matter what actual type it is. New countries are easily added by just creating new classes that implement Zip. Using an interface versus an abstract parent class leaves you free to extend another class, because you haven't exhausted your one-parent-only rule in Java.

Also, try to avoid hard-coded values as much as possible. Rather, use parameters to methods and constructors to allow values to be passed in at runtime. For example, don't hard-code a tax rate. Instead, accept it as a parameter or, ultimately, read it from a database so you can easily change it on the fly.

This will all come with time, and you will find yourself going back with experience and changing classes and class hierarchies to be more adaptable. Rather than overwhelming you at this point with a complex example of a "good" object-oriented system, we will leave that to other books. We recommend you go to an online bookstore like www.amazon.com and search for books related to "object-oriented," "design patterns," and "business objects."

Other OO Terms

Framework is another term you will hear often. It typically refers to a collection of related classes that work together to model a generic problem or domain. Frameworks also usually exhibit the behavior of being easily tailored to your particular situation with relatively small, well-defined efforts. Further, your extensions are usually called by a framework (via polymorphism) instead of the framework supplying APIs that your code calls. If it calls you, versus you calling it, then it is probably a framework.

The ultimate example of a set of frameworks in Java is the IBM SanFrancisco project, which is a large undertaking to supply a framework of business objects written in Java. The idea is that these frameworks can supply up to 40 percent of a typical business application. See the Web site www.ibm.com/java/sanfrancisco for more information.

Another term commonly heard in the world of OO is object factory. This is simply a term for a class or method that returns a new instance of another class. Like a factory, it spits out things—in this case, instances of a particular class.

Something we find exciting is the relatively new concept of design patterns. These are the result of efforts to look at many existing OO solutions to common programming or design problems, and extract from these real-world examples of OO the designs for common programming problems. Because OO is an "experience-won" skill, the books and articles detailing design patterns that others have used to solve typical challenges offer a tremendous leg up to those starting out in OO, or starting a new OO project.

Finally, you will hear about business objects a lot. These are typically classes that are designed to represent a particular business entity, such as a customer, an invoice, or a policy. The cool thing about them is that they hold inside them all the knowledge needed to populate themselves from a database (or other persistent store) and subsequently write themselves to a database. Of course, this usually involves reading and writing many tables or files, and it usually involves dealing with the commitment and rollback of that database access as a one- or two-phase commitment. In other words, it is transaction-suave.

Business objects hide all that complexity. Once figured out and coded internally, they make it very easy to write applications that simply use these objects, allowing you to be blissfully unaware of the underlying complexity. Many books are available to help you design and code these essential classes, and many tools are available to help. Business objects are also the domain of Enterprise Java Beans (EJBs), the Java standard that many companies now support via runtimes and tooling. IBM's runtime is WebSphere Application Server, Advanced Edition or Enterprise Edition. IBM's tooling for EJBs is VisualAge for Java, Enterprise Edition.

OO Design Tips

"To inherit or not to inherit, that is the question." The following are some tips about when and how to use inheritance.

Tip #1: Use the Is a Rule with a Grain of Salt

Despite the is a rule, do not inherit from an existing class unless you intend to override one or more methods. For example, the user interface class in Java, AWT (Abstract Windowing Toolkit), has a Frame class that creates a frame window. Your windows will use this and add their own buttons, entry fields, and so on. So, should your window classes inherit from Java's Frame class?

Certainly, your new window is a frame. However, there are no methods in Frame you typically need to override, so you sometimes are better off including an instance of Frame in your class. This keeps you from using up your single allowance for inheriting. However, you should supply a getFrame method that returns the Frame object so users can call its methods if they need to.

There is no right answer here, it depends on your situation.

Tip #2: Create Child Classes that Add Functionality

Despite the terminology, child classes should offer a superset of functions compared to their parent or superclass. That is, the child class (the one with the extends keyword) should add methods over the parent class, not remove them. If you find yourself wanting to remove methods from your parent class because they do not apply to your child class, you probably should not be inheriting from that parent. Even if, in real life, there is an is a relationship, there might not be one in the actual code.

For example, a manager might not be an employee, if class Employee has methods dependent on hourly wages versus salary. You might have to abstract-out a third common class called HR (for "human resource") from which Employee and Manager both inherit. (Of course, many would argue that the is a relationship does not apply to managers and humans!)

Another option here is to have a class called JobDescription that ManagerJob extends, and then to contain an instance of ManagerJob in the appropriate Employee objects. This gives you the flexibility of defining additional employee job descriptions and supporting multiple job descriptions per employee.

Tip #3: Strive for Easy and Elegant User Code

When designing your class hierarchies and classes, keep the users of your classes in mind. You want to minimize their effort. The main part of your application should be as simple and elegant as possible. This means letting your classes do as much as work as possible. For example, it might be as simple as this:

TheApp app = new TheApp();
app.run();

Tip #4: Strive for Small Methods

If you find yourself with a very large and complicated class that has some really outrageous methods, you should look hard at it. You might need to abstract-out some code into a base or helper class, and use more and smaller methods. This makes the code easier to maintain and understand.

Tip #5: Balance Anticipated Function versus Bloat

The trick for long-lasting classes is to embellish them with as many methods as you predict might ever be needed, but don't go overboard. Do all private variables really need setXXX methods? Will someone really want to change these variables? Do you need a particular new method, or can the result be achieved by using a combination of existing methods?

Tip #6: Be Wary of Large Numbers of Instance Variables

If your class has dozens of instance variables, you should look at grouping those variables into classes of their own and instantiating instances of them. Remember, classes do not have to have methods. Moving related variables into their own class will make your code more elegant, and you might discover that those helper classes have other uses, too. You might also discover that some of the manipulations you were doing directly on those variables can be moved into methods of the new class.

Tip #7: Watch for Static Candidates

Do you have methods that do not act on any of your class instance variables? Maybe you wrote some utility methods that take all their information as parameters? If so, either forgo the parameters and use instance variables instead, or make the method static so it can be used by others without requiring an instantiation of your class.


Summary

This chapter is a whirlwind introduction to the world of object orientation. It briefly introduced you to the following OO terminology:

  • Encapsulation, inheritance, and polymorphism.

  • Has a, is a, and uses relationships.

  • Methodologies for specifying and documenting object-oriented systems.

You were also introduced to some new Java constructs:

  • The extends keyword.

  • Interfaces and the implements keyword.

  • The instance of operator.

  • The protected, abstract, and final modifiers.

You also learned about the following:

  • The Object and Class superclasses.

  • The Cloneable interface and the clone method.

  • The Enumeration interface.

  • Frameworks, object factories, design patterns, and business objects.

A full introduction to, and coverage of, object-oriented analysis and design is beyond the intended scope of this chapter, but you should feel more acclimatized to OO now, and be ready to pursue it further with subsequent books. Remember, not everyone on the project needs to be an OO genius. Every Java programmer, however, should understand the basic syntax and ideas presented here.


References

Gamma, Helm, Johnson, and Vlissides. 1995. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0201633612.

Gilbert, Stephen, and Bill McCarty. 1998. Object-Oriented Design In Java. Waite Group Press. ISBN 1571691340.

Larmon, Craig. 1997. Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design. Prentice Hall. ISBN 0137488807.


Chapter 10: Exceptions

You are, no doubt, familiar with the concepts of exceptions and conditions as a COBOL programmer. They have been part of application architecture for some time and were a harbinger of things to come. Well, now their time has arrived! Let's begin by reviewing, briefly, the OS/390 Language Environment and the OS/400 Integrated Language Environment exception architecture, with a view to comparing them later to Java.

COBOL Exception And Error-Handling: The OS/390 LE And OS/400 ILE Exception Model

When writing new LE and ILE programs, the exception model (or condition model, if you prefer) is as follows:

  • The original exception (or condition) is passed down the call stack, from the receiver of the exception back, until a handler is found for it (that is, code that is willing to handle it).

  • If the original message is handled by somebody on the call stack, the entries above it are terminated.

  • If nobody on the call stack handles this message, it is converted to a T_I_U (Termination Is imminent due to Unhandled) condition on OS/390, or a function check (CPF9999) on OS/400, and the process is repeated for this new condition.

  • Each entry from the receiver down is then given a chance to handle the function check or T_I_U.

  • Each entry in the call stack that does not handle the function check is typically removed from the call stack (depending on the user answer to an inquiry message), and the next entry is given a chance to handle it.

Each program on the call stack in LE and ILE can have its own unique exception-handling support. This is enabled by calling the CEEHDLR condition-handling service API to register a user-defined condition-handling procedures, and CEEHDLU to unregister. These should be called at the beginning and end of the routine (COBOL program) for which you want to enable your own exception-handling. Your routine can decide whether to handle (resume), pass on (percolate), or change (promote) the exception. Your COBOL code can also signal or send its own conditions, by using the CEESGL condition-handling service API.

COBOL itself offers a number of ways to deal with errors or exceptions:

  • Clauses on language statements.

  • ERROR declaratives.

  • File status data items.

  • System API calls to register condition handlers.

First, the language statements themselves catch many of the common exceptions, and allow you, via optional clauses, to run your own code when these happen. These clauses include the following:

  • ON SIZE ERROR on STRING and UNSTRING statements.

  • ON SIZE ERROR on ADD, SUBTRACT, DIVIDE, MULTIPLY, and COMPUTE statements.

  • ON EXCEPTION on CALL statements.

  • AT END on READ statements.

  • AT INVALID KEY on READ, WRITE, REWRITE, UPDATE, and DELETE statements.

For exceptions or errors during input/output processing, you can also do the following:

  • Specify the USE AFTER ERROR declarative statement and procedures to link an error procedure to runtime errors with a particular file, or a particular file mode (INPUT, OUTPUT, I-O, or EXTEND).

  • Specify the FILE STATUS clause on the SELECT statement in the FILE-CONTROL SECTION, identifying a two-byte data item that will be updated by COBOL after each I/O operation.

We won't cover all of these here, because you are probably familiar with them. COBOL programmers, perhaps more than any others, write robust, fault-tolerant code.


Exceptions In Java

The OS/390, OS/400, and COBOL exception model has taught you to be disciplined when it comes to proactively designing support for error situations. If you don't follow this practice, you risk exposing those ugly program abends to your customers. Doing more work up-front prevents problems in the long run, producing more robust, fault-tolerant code that is cheaper to maintain. (So, it is safe to say that COBOL programmers are exceptional!) These noble goals have been taken to heart by Java's designers.

Java also provides the feature of exceptions for unexpected situations, and it has language constructs for sending and monitoring them. The consequences of ignoring them are even more frightening than in COBOL. Java goes a step further than simply ending your program at runtime if you fail to monitor for an exception that happens. It actually tries to catch, at compile time, where you missed coding monitors for potential exceptions. To accomplish this, it has further language syntax to define, for each method, the exceptions that callers of a method need to monitor for.

Exception Objects in Java

Java provides the concept of exceptions, similar to conditions in the LE and ILE condition model. These exceptions are Java objects, of course. There is a Java-defined class called Throwable, which all Java exception classes extend. This class is in the Java-supplied package java.lang. Any Java class that directly or indirectly extends Throwable is an exception in Java, whether that class is Java-supplied or written by you. You use unique language syntax to send these exceptions back up the method call-stack, and to monitor for them in methods you call.

Objects of the Throwable class, or by inheritance any child class, contain a string describing the exception, which is retrievable with the getMessage method. Another useful method in this class is printStackTrace, which prints out to the console a method call-stack trace from the point where this exception was sent. Java programmers are particularly fond of this latter method. It is very useful in debugging. Here is an example of printStackTrace.

java.lang.NumberFormatException: abc
   at java.lang.Integer.parseInt(Compiled Code)
   at java.lang.Integer.parseInt(Integer.java:458)
   at ShowPrintStackTrace.convertStringToInt(ShowPrintStackTrace.java:20)
   at ShowPrintStackTrace.main(ShowPrintStackTrace.java:8)

This is from the exception NumberFormatException that the method parseInt in class Integer throws when it is given a string to convert to an integer, and that string contains nonnumeric characters. You can see that the stack trace starts with the name of the exception class, followed on the same line with the message text from the exception object (in this case, "abc," which is the invalid input used). Following that is the method call-stack, starting with the method that threw the exception. In this case, it is the JDK-supplied method, which is listed twice, indicating the method calls itself recursively. The stack trace ends with the method that called printStackTrace (in this case, the main method in the ShowPrintStackTrace class).

ILE and LE conditions have a severity associated with them, and a unique condition ID for OS/390 (inside the condition token) and message ID for OS/400. Java exceptions (or Throwable objects) have this information, too. The severity and unique ID is implicit in the particular class of the exception object. In other words, there are many exception classes extending the Throwable class, so the exact error can be determined by the exact exception object that was thrown. The class itself is equivalent to a message ID or condition ID because it uniquely identifies the error.

There is no explicit severity associated with an exception in Java, but you can think of the subclasses of Error as being critical, RuntimeException as being severe, and all others as being normal. (These subclasses are described shortly.) There are no informational or warning exceptions, since all Java exceptions can cause a program abend if they are not prevented or handled. Regular return codes are used instead of exceptions for informational and warning situations. Java supplies a number of subclasses of Throwable for many specific errors and groups of errors. In addition to the ID and severity that the class type implies for Java exceptions, they also have message text associated with each exception object, which is retrieved using getMessage. This is also true of LE/ILE conditions; they have message text associated with them, which is often written to the console or message file log.

The primary subclasses of Throwable are Error and Exception. Error exceptions are typically nonrecoverable system errors, such as "out of memory." Exception errors are further subclassed by RuntimeException and other classes, as shown in Figure 10.1. RuntimeException errors are programming errors you make, such as an array index out of bounds. (Hey, it happens.) The other subclasses of Exception are, typically, related to preventable user-input errors.

Click To expand
Figure 10.1: The major child classes of the Java Throwable class.

You typically do not monitor for Error exceptions or subclasses in Java. For one thing, you usually can't do much about them. What's more, you will never send one of these exceptions yourself. These exceptions are sent only by the system. You need to be concerned with Exception exceptions (good name, is it not?) and their subclasses—both for sending and for handling.

You have our permission to also ignore RuntimeException and its subclasses. These are used by Java to tell you that you made a programming error, not for you to tell others that you made a programming error. So, your code will probably send and handle only subclasses of Exception, except those that subclass RuntimeException. There is little point in listing all of the subclasses here because, as you will see, every class you use clearly documents the Exception subclass objects it might send. You will learn these, as you need them. We have no doubt that you will need them!

The last point to make about Exception objects (since it is only Exception objects that you really care about) is that you can define your own. You will probably need to do this in Java if you are writing robust code or, more precisely, when you are writing robust code. If you discover an unexpected error in your error-checking code, such as bad input or an expected resource not found, you should send an exception instead of using a return code. Return codes, such as integer values, should be used to identify valid possible results, not to identify exceptional situations.

For example, an end-of-file is a valid possible result, while a file-not-found is an exceptional situation. The first will almost always happen; with good input, the latter should almost never happen. It is, in other words, a frequency call. Having decided that you should send an exception, your next step is to peruse the Java documentation for an existing Exception subclass that applies to your situation.

Searching Java Documentation for Classes

It is time that you learned how to find the Java Development Kit's documentation for a particular class. In this case, we are interested in seeing a list of the classes that extend the Throwable class in the java.lang package, since that shows all the available pre-defined exceptions in Java. This is important when you are writing your own Java code, so you can look for existing exceptions for your code to throw. Hopefully, the name of the exception class will give a clue to its intended use, and from there, you can drill down to the detailed documentation about that class.

First, you must have downloaded and unzipped the JDK documentation file. When using WinZip to do this, just specify the c:\ drive or the drive you installed the JDK itself on, and click the "use folder names" checkbox from the WinZip Classic interface. Next, navigate to the \docs\api subdirectory of the jdk folder, as shown in Figure 10.2.

Click To expand
Figure 10.2: Where to find JDK documentation.

In that subdirectory, double-click the index.html file to open it in your default browser. For us, that is Netscape, as shown in Figure 10.3.

Click To expand
Figure 10.3: The index.html file from the \docs\api directory.

You should see the underlined word Tree in the right frame, at the top, in the center. Click it to go to the list of all packages and classes, as shown in Figure 10.4.

Click To expand
Figure 10.4: After selecting the Tree link, all packages are listed.

Now, again in that right frame, click the package java.lang, listed under the heading Package Hierarchies, to see the class hierarchy for that package. Scroll down until you see the java.lang.Exception class, as shown in Figure 10.5.

Click To expand
Figure 10.5: The class hierarchy for the children of the Exception class.

This shows you a nice tree view of how classes extend each other. Remember, every class that extends Exception (but not RuntimeException) is an exception you might potentially use. To get more details on a particular class, just click its name.

Creating Your Own Exception Subclasses

If you find an existing exception that meets your needs, such as IOException, use it and send an object of that class for your exception. (Instructions on performing this procedure follow.) If you cannot find one that will work in your situation, create a new class that extends Exception or one of its children, as shown in Figure 10.6. Just as you would never send your own instance of Error or RuntimeException exceptions, you would never create your own child subclasses of these.

Click To expand
Figure 10.6: Where to create your own exception classes.

For example, you might define the following:

public class BadZipCode extends Exception
{
   ...
}

Because it is your own class, you might define constructors that take additional useful information, such as the method name where the error occurred. This information could be beneficial to the code that receives such an exception if you also supply a getMethod method. You might also decide to record the bad zip code number, as in the example in Listing 10.1.

Listing 10.1: Defining a new exception.
Start example
public class BadZipCode extends Exception
{
   private String badzip;
   private String method;

   public BadZipCode(String badzip, String method) // constructor
   {
      super("Bad zipcode '" + badzip + "' given.");
      this.badzip = badzip;
      this.method = method;
   }
   public String getBadZip()
   {
      return badzip;
   }
   public String getMethod()
   {
      return method;
   }
} // end class BadZipCode
End example

Keep in mind that when one class extends another, the child class inherits all the methods of the parent class (Exception, in this case). This means your new class will have the getMessage and printStackTrace methods of the grandparent Throwable class. For the getMessage method to return meaningful information, you have to supply it when constructing the exception object to throw, as well. This is done by passing the message text as a parameter to the Throwable parent constructor (which is always the ultimate parent), as was done with the super call in the constructor in Listing 10.1.

Sending Java Exceptions—The throw Statement

Having decided that you will send an exception in your error-checking code, how do you do it? First, you have to instantiate an instance of the particular Exception child class, of course. Then, you use the Java throw operator. (These steps can be combined into one statement.) The example in Listing 10.2, for example, detects an error in input and throws an instance of the new BadZipCode exception class.

Listing 10.2: Throwing an exception.
Start example
public static void myMethod(ZipCode zipcode)
{
   if (!zipcode.isValid()) // ZipCode class and isValid method not shown here
      {
         BadZipCode exc = new BadZipCode(zipcode.toString(),"myMethod");
         throw(exc);
         //or just: throw(new BadZipCode(zipcode.toString(),"myMethod"));
      }
   ...
}
End example

The throw operator is similar to CL's SNDPGMMSG command on OS/400, or the CEESGL (signal condition) API in ILE or LE. As this example shows, you can either send an instance of one of Java's predefined exception classes (such as IOException), or you can send an instance of your own exception class (such as BadZipCode).

Typically, you should try to use an existing class that matches your error situation. Don't create your own unless you can't find an existing one that meets your needs. You might choose to create your own class so that you can supply as much information in it as you want. This is comparable to deciding whether to use a supplied CPF or MCH message on OS/400, or to create your own new message in your own message file.

By the way, for OS/400 programmers, the generic message CPF9898 ("&1") that many of us use on OS/400 is similar to using the generic Exception class in Java. On OS/400, you substitute your own message text in CPF9898. You can do the same in the constructor of Exception, as shown here:

throw (new Exception("You made a big mistake there pal!"));

It is important to remember that you never throw exceptions that are of type Error. Error exceptions are for dramatic system errors. They are thrown only by the system.

What does throw do? It ends your method, that's what! Any code following the throw statement is not executed. You have done the equivalent of using CEESGL to signal a severity-4 condition in LE, or sending an escape message in an OS/400 CL program. The current method is removed from the stack, and the exception is sent back to the line of code that called this method. If that code does not monitor for this exception, the method it is in is also terminated. The exception is sent back to the caller of the method, just as in ILE or LE exception/condition percolation. It continues until it finds an entry in the call stack that handles this exact exception (or one of the parents of this exception, as you will see).

Monitoring for Java Exceptions—The try/catch Blocks

Now that you know how to send or throw an exception to the callers of your code when you have detected an error, let's discuss what those callers do to monitor for it or to process it. To monitor for an exception, there is additional Java language syntax. Keep in mind that, in Java, you can place multiple lines of code in its own block (with its own variable scope) using braces, as follows:

{
   // one or more statements of code
}

The Java syntax for monitoring messages builds on this, allowing you to specify a try/catch combination, as follows:

try
{
   // try-block: one or more statements of code
}
catch (Exception exc)
{
   // catch-block: code to handle the exception
}

The idea is to place any method call statement that might throw exceptions inside a try block. Because it is a block, you can actually place one or more statements inside it. If any of the statements inside the try block does throw an exception, the catch block will get control. Any code inside the try block after the exception-throwing call is not executed. Control flows immediately to the catch block upon receipt of a thrown exception.

The catch block defines a parameter, which is the exception it will handle. Java passes that exception object at runtime if an exception is thrown. Your catch-block code can use methods on the object to display information to the end-user, if desired. For example, you might have something like this inside your catch block:

System.out.println(exc.getMessage());

Recalling the zip code example, here is what the caller code might look like:

try
{
   ...
   myObject.myMethod(zipcode);
   ...
}
catch (BadZipCode exc)
{
   System.out.println(exc.getMessage());
   System.out.println("... in method " + exc.getMethod());
}

Java is similar to COBOL in this respect. You have user-defined or system-supplied code that might possibly send (throw) exceptions, using the CEESGL API (or on OS/400, the SNDPGMMSG CL command). If the caller of that code doesn't monitor for that exception, its call-stack entry will be removed, and the next call-stack entry will be given the exception. The call stack is peeled back until a handler is found, or the whole application ends.

Monitoring for Multiple Java Exceptions

Although both try and catch are necessary syntactically, it is the catch statement that tells Java which exception type you are monitoring for. If your try block gets an exception that the catch statement did not identify in its parameter (for example: catch (MyException exc), it is as though you never had the try/catch block. Your method is ended and the exception is sent back to the previous call-stack entry.

What if you call a method that throws more than one possible exception? How do you define the catch statement when you need to monitor for multiple possible exceptions? For instance, in our example, what if myMethod could throw either a BadZipCode exception or an IOException. In the latter case, your catch block will not get control because you told it to monitor only for BadZipCode exceptions. The following two options exist:

  • If it doesn't matter to you which exception happened, just that some exception happened, define a parent exception class type on the catch. The catch will actually get control of any exception that is of the defined type or lower in the hierarchy. To catch all exceptions, specify the root parent of all catchable exceptions: Exception.

  • Define multiple catch blocks after the try block. This is perfectly legal. The exception object received will be compared to each catch statement's parameter, in turn, until a match on type is found (or the catch specifies a parent class of the thrown exception class type). Use this technique when it is important to your error-recovery code to know exactly what exception was thrown.

An example of calling methods is shown in Listing 10.3.

Listing 10.3: Calling methods that can throw exceptions: try and catch blocks.
Start example
try
{
   //...
   myMethod(zipcode);
   //...
}
catch (BadZipCode exc)
{
   System.out.println(exc.getMessage());
   System.out.println("... in method " + exc.getMethod());
}
catch (IOException exc)
{
   System.out.println(exc.getMessage());
   System.out.println("Bad input—naughty, naughty!");
}
End example

The finally Block

Finally, there is finally. This is an optional block you can define at the end of all your catch statements:

try
   block
catch (exception-type-1 identifier)
   block
catch (exception-type-2 identifier)
   block
finally
   block

You might think this will get control in an exception situation if none of the catch statements handles that particular exception type. This is only partly correct, however. The finally block, if present, is always executed. That is, it is executed whether or not an exception was received in the try block, and whether or not a catch block processed it. For example, if a BadZipCode exception is thrown by code in the try block, the code inside the BadZipCode catch block will be executed, as well as the code inside the finally block.

The finally block is typically used for code that has to be done no matter what, such as closing any open files. Notice that no statement inside a try block, not even a return, can circumvent the finally block if it is present. If the try block does do a return, then the finally block will be run and the try block's return statement will be honored. (It is possible to override the try block's return value in the finally statement by coding another return statement.)

You can also throw exceptions in your class constructor code. This has the effect of canceling the instantiation or new attempt. In this case, users of your class must place the new operator inside a try block.

Who Throws What

How many times have you wished for an automated way to determine the list of exceptions that an API, service, or command might send? For example, it would be nice to have a tool that returned a list of all the possible messages or conditions that a particular API might send. OO programmers face a similar problem when trying to determine the exceptions any particular method call might result in. Java designers thought about this problem. They knew that if they didn't come up with a solution, the exception architecture in Java would suffer two real-use problems:

  • Programmers would not use it enough, which would lead to too much error-prone code (human nature being what it is).

  • Programmers who did decide to place sensitive method calls inside try/catch blocks would find it painful to determine what exceptions each method could possibly throw. They would be dependent on those methods having the proper and up-to-date documentation available to them.

So, Java designers decided to force method designers to specify up-front, in the method signature, what exceptions are thrown by that method. This is done by specifying a throws clause on your method signature:

public void myMethod(ZipCode zipcode) throws BadZipCode
{
   ...
}

You must specify this if you explicitly throw an exception, or your compile will fail. Furthermore, your compile will fail if you call a method that throws an exception and you do not have a try/catch block for that explicit exception (or one of its parents). The compiler is able to catch this because it knows all the methods you call. It also knows, via the throws clause, what exceptions those methods throw. Isn't that nice? You have compiler help to write robust code. You are forced to handle any exceptions produced by code that you invoke, either with an explicit try/catch block, or by listing the exceptions in your method's throws clause. What's more, you are forced to tell the world what exceptions you throw yourself.

The throws clause, then, is a form of forced documentation for your code that makes it easier for callers to handle your exceptions. It forces the callers to handle your exceptions. It is possible to list multiple exception classes after the throws keyword by separating them with commas:

public void myMethod(ZipCode zipcode) throws BadZipCode, IOException
{
   ...
}

Actually, it is not exactly true that you have to define in the throws clause every exception that called-code may produce. You only need to document the exceptions you do not explicitly handle with a try/catch block. Keep in mind that if you call a method and it throws an exception that you do not handle (via try/catch), it is percolated up to the caller of your method. You might very well choose to do this in cases where there is no reasonable action you can take to recover from the exception.

Sometimes, you might decide to handle the exception, but then throw it again, anyway. This is legal, and can be done with a simple throw exc; statement in your catch block. In this case, because you are throwing this exception (albeit, again), you must define it in your method's throw clause. Again, if your method code does not monitor for an exception it might receive, you must specify that exception in your method's throws clause, in addition to any exceptions your code explicitly throws.


Summary

This chapter covered the following:

  • A review of the OS/390 LE and OS/400 ILE condition model, and COBOL error-handling.

  • An introduction to the Java exception model.

  • Java's Exception class hierarchy.

  • The Java throw statement, which is like OS/400 CL's SNDPGMMSG or ILE/LE's CEESGL API.

  • The Java try/catch/finally statement, which is like OS/400 CL's MONMSG or ILE/LE's CEEHDLR API.

  • The Java throws clause for method signatures.

  • The two popular methods that all exceptions have: printStackTrace and getMessage.

  • How to write your own exception classes by extending the Exception class or one of its children.

  • How to find Exception-class child classes in the JDK documentation.

  • The two hierarchies into which Java-supplied exceptions are divided: those that extend Error and do not need to be monitored, and those that extend Exception, and do need to be monitored.


Chapter 11: Threads

In contrast to the exceptions covered in chapter 10, the concept of threads is usually foreign to a COBOL program. So, this chapter focuses mainly on Java, without pro- viding many COBOL comparisons. We predict that you will find this new concept interesting and that there will be increasingly more discussions of threads on OS/390 and OS/400, especially since they are available on both systems now.

Synchronous Versus Asynchronous

In COBOL, you call paragraphs and programs synchronously, that is, the code that makes a call does not get control back until the called code has completed. For example, let's say that program P1 calls program P2 using the CALL statement. Execution of P1 will stop at the CALL statement to wait for the execution of P2 to end and return before the code after the CALL statement can be executed. A similar situation exists with the PERFORM statement for paragraphs. This is also true of Java method calls, such as myObject.myMethod(), as you have seen.

Java, however, also has built-in support for asynchronous calls. These are calls that spawn a second thread of execution and return immediately. Both the calling code and the called code run at the same time (concurrently). With enough threads of execution, you could have a whole suit. Spun by the collar. (Sorry, bad pun.)

In order to distinguish between a traditional synchronous call and a threaded asynchronous call, the latter is often referred to as "spawning" instead of "calling." A time-graph that shows which method is currently executing would look something like Figure 11.1.

Figure 11.1: Synchronous versus asynchronous calls.

It is important to note that when a method is spawned as a thread, it runs at the same time as the code after the spawn statement. (You will see the syntax of doing this shortly.) Which one ends first is not predictable, and your spawning code does not get the return value from the thread. This is be- cause the spawn statement returns immediately-before the called code has even run. This is quite similar to submitting a program call to batch in COBOL via PBXBATCH on OS/390 or SBMJOB on OS/400, and is in contrast to using the CALL statement.


Threads Versus Jobs

Asynchronous execution is not totally foreign to COBOL programmers. In fact, it is done quite often. Many interactive applications have a print function or menu option that submits a job to batch, instead of performing it interactively. This allows the user to get control immediately, while the print job runs quietly in the background. This is a disconnected job in that the application does not care when it ends. It merely submits it and forgets about it. Some applications that involve numerous screens of input for a single transaction also run a job in the background, gathering information from the database, so they can be shown to the user by the time the user reaches the final screen. This is a connected job because the main interactive job must "synch up" with the background batch job by the final screen. It is usually done using some form of inter-job communication.

Jobs on OS/390 and OS/400 are similar to processes on other platforms. They differ from threads in Java in terms of overhead, resource sharing, and synchronization. To start a new job requires a significant amount of system resources, as you know. Calling another program in the same job is expensive enough. Starting another job altogether is considerably more expensive. There is overhead in allocating storage, setting up job attributes, loading system resources, and so on. You would not do this without due consideration, and certainly not for frequently repeated application functions.

In Java, the equivalent of starting another job would be starting another Java Virtual Machine. Using the system's command analyzer, you would invoke another Java program (via Java MySecondClass, for example). For information on how to invoke another job from within Java (say, to call a COBOL program), see chapter 14.

Threads, on the other hand, have relatively little overhead because they share everything with the other threads in that job or process. This includes all global (that is, instance) variable data. No new memory is allocated for the secondary threads. Even if you do not spawn a thread, your main code is considered to be running in a thread (the primary, or main, thread).

Methods invoked in another thread each get their own copy of local variables and parameters, as you would expect for a method. But instance variables, which are defined at the class level and are equivalent to WORKING-STORAGE data items, are shared. Think about the fact that spawning a method returns control to you immediately. This means you could immediately spawn another method-or even the same method a second time. If you spawn two methods for the same instance of a class, they both have the same copies of the instance variables in that class. If one changes an instance variable, it is changed for the other, too. Figure 11.2 depicts this sharing. You will see more about this sharing of instance variables and its implications later in the chapter.

Click To expand
Figure 11.2: Threads sharing instance variables.

To use threads efficiently, of course, the underlying operating system must have true built-in support for threads of execution, not just jobs or processes. Java cannot do it on its own. All major operating systems today support native threads (as opposed to simulated threads or lightweight jobs). This includes OS/390, which first added thread support for its UNIX System Service, and OS/400 as of V4R2, as part of its built-in robust Java support. Both operating systems allow threads to be created from C, C++, or Java applications. Typical operating-system thread support includes the ability to start, stop, suspend, query, and set the priority of the thread. You might give a print job low priority so that it gets only idle CPU cycles, for example. The Java language has built-in support for all of this in its thread architecture.

Questions arising at this point might include the following:

  • How do I call a method asynchronously (that is, spawn a thread)?

  • How do I stop a thread?

  • How do I get back information from that thread?

  • If necessary, how do I wait for that thread to end?

  • How do I temporarily suspend a thread?

  • How do I change a thread's priority?

  • How do threads exchange information with each other?

All of these questions will be answered in this chapter. Don't worry-we won't leave you hanging by a thread!


Calling A Method Asynchronously: Spawning Threads

Imagine you have a method that you want to run asynchronously, to improve response time for the end user. For convenience, let's use a method called printReport, invented for this example. It is potentially a long-running method, well-suited to being a background thread.

An Example without Using Threads

The method, and its class (PrintReport), is shown in Listing 11.1. The printReport method takes an array of objects, which implement the Printable interface. This is an interface that defines a method called print, which takes no parameters and returns nothing.

Listing 11.1: The PrintReport class.
Start example
public class PrintReport
{

   public void printReport(Printable[] objects)
   {
      for (int index = 0; index < objects.length; index++)
      objects[index].print();
   } // end printReport method

} // end PrintReport class
End example

The interface is not important for our discussion here, but this is what it looks like:

public interface Printable
{
   public void print();
}

So, the method takes an array of any objects whose class implements this interface. Listing 11.2 shows an example from a class named PrintableEmployee.

Listing 11.2: The print method from the PrintableEmployee class.
Start example
/**
* Method print, as required by Printable interface
*/
public void print()
{
   System.out.println("Confidential");
   System.out.println("Employee ID, dept, name: "+id+","+dept+","+ name);
   System.out.println("Started on.......: " + startDate.getTime());
   System.out.println("Current salary...: " + salary);
   System.out.println("Current level....: " + level);
}
End example

The important thing about the printReport method from Listing 11.1 is that it could potentially be given a huge number of objects, or objects that have very time-consuming print methods. You just can't be sure how long it might take, so if you call it from a Print button in a GUI, for example, you probably ought to be doing it asynchronously. You'll see how to do that soon.

First, to test the printReport method synchronously, let's write a main method in the PrintReport class that will instantiate some Printable objects and call the method. Later, you can see it evolve into an asynchronous call.

Listing 11.3 shows the full PrintReport class.

Listing 11.3: The full PrintReport class.
Start example
import java.util.*; // for GregorianCalendar class
import java.math.*; // for BigDecimal class

/**
* This class prints a given array of printable objects
*/
public class PrintReport
{

   public void printReport(Printable[] objects)
   {
      for (int index = 0; index < objects.length; index++)
      objects[index].print();
   } // end printReport method

   public static void main(String args[])
   {
      PrintableEmployee employees[] = new PrintableEmployee[2];
      employees[0] = new PrintableEmployee(000001, "Phil Coulthard",
                                          new GregorianCalendar(1986,02,30),
                                          new BigDecimal(10000.00), 9, 522);
      employees[1] = new PrintableEmployee(000002, "George Farr",
                                          new GregorianCalendar(1984,05,01),
                                          new BigDecimal(10000.00), 9, 523);
      // create instance of this class...
      PrintReport reportObject = new PrintReport();
      System.out.println("*** EMPLOYEE REPORT ***");
      // call printReport method passing our array...
      reportObject.printReport(employees);
      System.out.println();
      System.out.println("*** END OF REPORT ***");
   } // end main method
} // end PrintReport class
End example

This full version has a main method that creates an array of PrintableEmployee objects, and instantiates the two objects for each element. (The sample data is kept small, for simplicity). It then instantiates an instance of the PrintReport class and uses the resulting reportObject object reference variable to call the all-important printReport method. The sample data, which is the array of two PrintableEmployee objects, is passed into the call. Notice that a heading line is printed before calling the printReport method, and a footing line after calling it.

Running this class gives the following output:

*** EMPLOYEE REPORT ***

Confidential
Employee ID, dept, name: 1,522,Phil Coulthard
Started on.......: Sun Mar 30 00:00:00 EST 1986
Current salary...: 10000
Current level....: 9

Confidential
Employee ID, dept, name: 2,523,George Farr
Started on.......: Wed Jun 01 00:00:00 EDT 1984
Current salary...: 10000
Current level....: 9

*** END OF REPORT ***

You see that we got our printed report. And, you see that Phil and George need raises!

It's time to build on this example by changing it to spawn the printReport so it runs asynchronously, rather than synchronously. There are exactly two ways to run a method asynchronously in Java. The one you choose depends on whether the class containing the method to be run asynchronously is free to inherit from another class or not. Recall from chapter 9 that if a class already inherits from one class, it cannot inherit from another, because Java does not allow multiple inheritance.

Spawning Option 1: Extend Thread

The method you want to run asynchronously might be part of a class that is not already extending another class, or it might not be written yet, which leaves you free to put it in a new class definition. In these cases, you may choose to extend the Java-supplied class Thread in the java.lang package. Here are the instructions to do this:

  • Add extends Thread to your class definition.

  • Define a method with signature public void run(). This overrides the empty run method in Thread. Because it takes no parameters, the input to it, if any, will have to be passed in through class instance variables.

  • To run the code, create an instance of the class and invoke the start method on it. This method is inherited from the Thread parent class. It merely invokes the run method, but asynchronously. Why not just invoke run directly? Because that would be a synchronous call. The start method Java supplies in the Thread class does the work of calling the necessary operating-system API to spawn an asynchronous thread. You have to call start and let it, in turn, call your run method. Figure 11.3 depicts this process. (It doesn't show the work that Thread has to do to go to the underlying operating system and ask it to spawn a new asynchronous thread. You don't need to worry about this underlying "plumbing" because Java has done it for you.)

    Click To expand
    Figure 11.3: Using the start method to spawn a method asynchronously.

Referring back to the PrintReport class from Listing 11.3, let's change it as shown in Listing 11.4. (Note that the class name has been changed to PrintReportThread1.)

Listing 11.4: The PrintReportThread1 class, to be run asynchronously.
Start example
import java.util.*; // for GregorianCalendar class
import java.math.*; // for BigDecimal class

/**
* This class prints a given array of printable objects, in a thread
*/
public class PrintReportThread1 extends Thread
{
   private Printable[] objects; // instance variable

   public PrintReportThread1(Printable[] objects)
   {
      this.objects = objects;
   }

   public void run()
   {
      printReport();
   }

   public void printReport()
   {
      for (int index = 0; index < objects.length; index++)
      objects[index].print();
   } // end printReport method

   public static void main(String args[])
   {
      PrintableEmployee employees[] = new PrintableEmployee[2];
      employees[0] = new PrintableEmployee(000001, "Phil Coulthard",
                                          new GregorianCalendar(1986,02,30),
                                          new BigDecimal(10000.00), 9, 522);
      employees[1] = new PrintableEmployee(000002, "George Farr",
                                          new GregorianCalendar(1984,05,01),
                                          new BigDecimal(10000.00), 9, 523);
      // create instance of this class...
      PrintReportThread1 reportObject = new PrintReportThread1(employees);
      System.out.println("*** EMPLOYEE REPORT ***");
      // call printReport method passing our array...
      reportObject.start(); // spawn the run method
      System.out.println();
      System.out.println("*** END OF REPORT ***");
   } // end main method
} // end PrintReportThread1 class
End example

The changes made to this class in order for it to run the printReport method asynchronously are as follows:

  • Change the class to extend Thread.

  • Define an objects array variable as an instance variable, so the printReport method would have access to it. We have to do this because run cannot take parameters (this is how it is defined in Thread).

  • Add a constructor that takes the array as a parameter, and sets the instance variable to it. Alternatively, we could supply a set method to set the array variable later, after instantiation.

  • Add a run method, which calls the printReport method. Since only run can be called asynchronously, it, in turn, has to call the method we really want to run asynchronously. Alternatively, we could rename printReport to run, but we prefer not to, because if we later want to support multiple asynchronous methods in the class, we could do so by using conditional logic in our run method to determine which method to call.

  • Change printReport to use the instance variable, instead of taking in a parameter.

  • Change the main method to pass the local array to the constructor, since that is how it gets to the printReport method now. We also change main to call the start method, instead of calling printReport directly. Remember that start is inherited from the Thread parent class, and will implicitly invoke the run method.

When we compiled and ran this version of the sample program, we got the following:

*** EMPLOYEE REPORT ***

*** END OF REPORT ***

Confidential
Employee ID, dept, name: 1,522,Phil Coulthard
Started on.......: Sun Mar 30 00:00:00 EST 1986
Current salary...: 10000
Current level....: 9

Confidential
Employee ID, dept, name: 2,523,George Farr
Started on.......: Fri Jun 01 00:00:00 EDT 1984
Current salary...: 10000
Current level....: 9

In this case, the calling code in main executed before the threaded method. You can tell this because the footer *** END OF REPORT *** printed before the report was printed. This is not guaranteed, though. The thread code could run after or during the running of the main code. It depends on the operating system's time-slicing algorithm for assigning CPU cycles to each running thread. The main code that spawned the thread is referred to by convention as the main thread of execution.

Spawning Option 2: Implement Runnable

It is possible that you will not have the option of changing your class to extend Thread because your class already extends another class. (The Java single-inheritance rule is that you can extend only one parent class.) Luckily, Java has an alternative to multiple inheritance in the form of interfaces, as discussed in chapter 9.

Here you will see another example of where Java puts interfaces to use in its own code. You can choose to implement the Java-supplied interface Runnable (also defined in the java.lang package). This option is just as easy to implement as the extend Thread option:

  • Add implements Runnable to your class definition.

  • Define a method with signature public void run(), which is the only method signature in this interface.

  • To "run" the code, create an instance of the Thread class, passing in an instance of your class to the constructor, and invoke the start method on that Thread instance. Figure 11.4 depicts this architecture.

    Click To expand
    Figure 11.4: Using the start method and the Runnable interface to spawn a method asynchronously.

Referring back to the previous PrintReport class, you would change it as shown in Listing 11.5 to use the Runnable option. (We renamed the class PrintReportThread2.)

Listing 11.5: Using threads by implementing the Runnable interface.
Start example
import java.util.*; // for GregorianCalendar class
import java.math.*; // for BigDecimal class

/**
* This class prints a given array of printable objects, in a thread
*/
public class PrintReportThread2 implements Runnable
{
   private Printable[] objects;

   public PrintReportThread2(Printable[] objects)
   {
      this.objects = objects;
   }

   public void run()
   {
      printReport();
   }

   public void printReport()
   {
      for (int index = 0; index < objects.length; index++)
         objects[index].print();
   } // end printReport method

   public static void main(String args[])
   {
      PrintableEmployee employees[] = new PrintableEmployee[2];
      employees[0] = new PrintableEmployee(000001, "Phil Coulthard",
                                          new GregorianCalendar(1986,02,30),
                                          new BigDecimal(10000.00), 9, 522);
      employees[1] = new PrintableEmployee(000002, "George Farr",
                                          new GregorianCalendar(1984,05,01),
                                          new BigDecimal(10000.00), 9, 523);
      // create instance of this class...
      PrintReportThread2 reportObject = new PrintReportThread2(employees);
      System.out.println("*** EMPLOYEE REPORT ***");
      // call printReport method passing our array...
      Thread threadObject = new Thread(reportObject);
      //reportObject.start(); // spawn the run method
      threadObject.start(); // spawn the run method
      System.out.println();
      System.out.println("*** END OF REPORT ***");
   } // end main method
} // end PrintReportThread2 class
End example

If you run this, you will get the same output as the TestReportThread1 version (so we don't show it again). The changes made compared to the TestReportThread1 version in Listing 11.4 are as follows:

  • Change the class to implements Runnable instead of extend Thread.

  • Change the main method to also instantiate a Thread object with your object as a parameter, and invoke start on that Thread object.

The two options, then, are extremely similar. Also, note that if you first choose to extend Thread, and then decide to change it later because you need to extend another class, implementing Runnable is very straightforward.


Stopping A Thread

You will find that your primary use of threads will be for putting long-running jobs in the background. This will improve the response time for your end users. You will also find that, in most such cases, you will want to give the user the option of canceling that long-running job. This is good user-in-control design, and your users will expect that kind of control. How many times have you decided to kill a compile job because you discovered an obvious bug in the source while waiting for the job to complete?

Let's say that you want to allow a long-running thread to be stopped. The typical mechanism is to use an instance variable that both the running, threaded method and the controlling thread (usually just the main or default thread) have access to. The controlling thread waits for a user indication that the running thread should be killed, and then sets the common instance variable to indicate this. Meanwhile, the method running in the thread periodically checks that variable and, if it is set, voluntarily ends itself by returning.

Suppose, for example, you have (admittedly contrived) code that loops for a given number of seconds and displays the elapsed seconds during each iteration. This code will be in a method called longRun, say, coded to allow the number of seconds to be passed in on the command line. The user is allowed to cancel the loop by pressing the Enter key on the command line. Listing 11.6 displays this stoppable-thread example.

Listing 11.6: A long-running method, and the required run method that calls it.
Start example
public class TestThreads extends Thread
{
   long seconds; // how long to run
   boolean stop = false;
   // constructor
   public TestThreads(long seconds)
   {
      this.seconds = seconds; // store time limit in inst var
   } // end TestThreads constructor
   // Mainline
   public static void main(String[] args) // cmdline entry
   {
      TestThreads thisObject;
      long longValue;
      if (args.length != 1) // verify a parameter passed
         {
            System.out.println("Please supply number of seconds");
            return;
         }
      try // verify parameter is numeric
      {
         Long longObject = Long.valueOf(args[0]);
         longValue = longObject.longValue();
      }
      catch (NumberFormatException exc)
      {
         System.out.println("Sorry- " + args[0] + " is not valid");
         return;
      }
      // create new instance of this class, passing in parameter
      thisObject = new TestThreads( longValue );
      System.out.print("Running... ");
      thisObject.start();// start the thread running...
      System.out.println("... press <Enter> to stop");
      try // watch for <Enter> key
      {
         System.in.read();
      } catch (java.io.IOException exc) {}
      thisObject.stop = true; // <Enter> pressed-turn on "stop" flag
   } // end main method
   // Potentially long running method
   public void runLong()
   {
      for (int secs=0; (secs < seconds) && !stop; secs++)
      {
         try
         {
            sleep(1000); // sleep for one second
            System.out.println(secs + " seconds");
         }
         catch (InterruptedException exc) { }
      } // end for-loop
   if (stop)
      System.out.println("... thread stopped");
   } // end runLong method

   public void run()
   {
      runLong();
   } // end run method
} // end TestThreads class
End example

If you run this program from the command line and pass in a maximum number of seconds, you will see that it prints out the current count every second, and stops when you press the Enter key:

>java TestThreads 30
Running... ... press <Enter> to stop
0 seconds
1 seconds
2 seconds
3 seconds
... thread stopped

The breakdown of the class is as follows:

  • The class inherits from Thread so that it can run one of its methods as a thread.

  • A constructor takes in, and stores in a class instance variable, a number representing the maximum number of seconds to loop.

  • The main method gets control from the command line. It validates the input and creates an instance of this class, passing in the user-supplied number.

  • The main method then spawns the thread by invoking the start method on the object instance, and does a read to wait for the user to press Enter. When this happens, the object's instance variable, stop, is set to true. Keep in mind that you can try this example as non-threaded by swapping the start method call with a direct call to longRun. You will see that, without the use of threads, the loop cannot be interrupted and canceled.

  • A longRun method, called by the required run method, loops for a given number of seconds. In each iteration, it "sleeps" for one second, using the static sleep method from the Thread class. If you chose implement Runnable instead of inheriting from Thread, you would code Thread.sleep(1000) because you would not inherit this method. Note that this method throws an exception, so it must be called in a try/catch block. The parameter to sleep is the number of milliseconds to sleep for. This is a friendly way to kill time because it gives other threads a chance to run.

  • The longRun loop will stop either when the maximum seconds is reached or the stop instance variable gets set to true.

This convention of using a mutually accessible variable to control the stopping of the thread works well in most situations. There are times, however, when it will cause a problem:

  • You have no convenient, mutually accessible variable to use for communications.

  • You have no easy way in the long-running code to check a variable in a timely manner.

These are cases in which you might find it necessary to forcefully kill a running thread. This is possible with the stop method inherited from the Thread class. Let's try this now, instead of the mutual variable method. You would change the main method line of code:

thisObject.stop = true;

to this instead:

thisObject.stop();

and then recompile and run. After a few seconds of running, press Enter. You should get the expected results:

>java TestThreads 30
Running... ... press <Enter> to stop
0 seconds
1 seconds
2 seconds

In fact, this time you might have noticed that it was even more responsive. Pressing Enter results in an immediate end to the program. Previously, it took up to a second to respond while the longRun method waited to wake up from its sleep.

The one potential downside of using stop is that the method did not get a chance to do any cleanup (in this case, to simply print out "thread stopped"). For example, you might need to close an open file. There is a way, however, to get control when you die.

The stop method works by sending an exception of class type ThreadDeath to the thread object it was invoked on (thisObject, in our example). Because this exception extends Error rather than Exception, you would not normally monitor for it. If you want to know when your code is being killed by the stop method, you can put the entire body inside a try/catch block, catching ThreadDeath. You must put the whole body inside try because you do not know which instruction will be running when the death knell comes. So, in this case, you would place the body of the runLong method in a try/catch block, as shown in Listing 11.7.

Listing 11.7: A long-running method that handles being prematurely stopped by the stop method.
Start example
// Potentially long running method
public void runLong()
{
   try
   {
      for (int secs=0; (secs < seconds); secs++)
      {
         try
         {
            sleep(1000L); // sleep for one second
            System.out.println(secs + " seconds");
         }
         catch (InterruptedException exc) {}
      } // end for-loop
   } // end try
   catch (ThreadDeath exc)
   {
      System.out.println("... thread stopped");
      throw(exc);
   }
} // end runLong method
End example

Now, when you run and cancel, you should see something like this:

>java TestThreads 30
Running... ... press <Enter> to stop
0 seconds
1 seconds
2 seconds
... thread stopped

Notice that the code re-throws the ThreadDeath exception after catching it. This is important, so that the thread continues to die as expected (with dignity).

If you implemented Runnable instead of extending Thread, you would invoke stop on the Thread instance instead of the class instance: threadObject.stop(). This is because stop is a member of the Thread class. When you extend Thread, you inherit it.

Note

As of JDK 1.2, the use of the stop method has been deprecated, meaning it is no longer recommended for use. When you compile code that does use it, you get this message:

Note: TestThreads2.java uses or overrides a deprecated API. Recompile with "-deprecation" for details.

Apparently, this is due to the concern that cleanup code could too easily be skipped over, leading to tough-to-find bugs. We recommend that you always use the first method we showed you-setting an instance variable and checking it regularly in your asynchronous code.


Stopping Multiple Threads-Thread Groups

The last example was easy, in that only a single thread was running. What if you instead started multiple threads running, and wanted to stop all of them at the same time? You could, of course, use either of the previous methods to stop each thread in turn. In real life, however, this can get messy because you might not know how many threads are running or have a convenient way of enumerating all of them. This is common enough, especially in Internet programming, where you might have numerous threads downloading images and resources.

Java designed in support for thread groups as a mechanism for partitioning threads into a uniquely named group, and allowing individual actions such as stop to be easily applied to all threads in the group. To create a thread group, you create an instance of the ThreadGroup class and pass in any unique name you want for the group:

ThreadGroup groupObject = new ThreadGroup("longRunning");

To identify that new threads are to be created as part of a particular thread group, you pass in the ThreadGroup object as a parameter to the Thread constructor:

Thread threadObject = new Thread(groupObject, thisObject);

Note that this works for both Runnable and Thread options. The Thread constructor that takes a ThreadGroup object parameter also takes a Runnable object parameter. If you use extends Thread, you simply pass an instance of your class, which automatically implements Runnable because its parent, Thread, does (that's the rule).

To test this, let's change the code from the previous example to use implements Runnable instead of extends Thread, and change only the main method as follows. (These changes are in the TestThreads3.java file on the CD-ROM accompanying this book.)

  • Create a ThreadGroup object, and create two Thread objects:

    thisObject = new TestThreads( longValue );
ThreadGroup groupObject = new ThreadGroup("longRunning");
Thread threadObject1 = new Thread(groupObject, thisObject );
Thread threadObject2 = new Thread(groupObject, thisObject );

  • Start both threads:

    threadObject1.start();
threadObject2.start();

  • Stop the ThreadGroup object instead of the Thread object:

    groupObject.stop();

Running this now gives the following result:

>java TestThreads 30
Running... ... press <Enter> to stop
0 seconds
0 seconds
1 seconds
1 seconds
2 seconds
2 seconds

You see each line twice because two threads were running. This ability to control multiple threads as a group is a welcome addition that Java offers above the typical operating-system support for threads. You will find it can save much ugly code. Note that the same object (thisObject) is used in this example, and two threads are spawned on it. This is quite legal, and quite common.

Note

The example shown here only catches ThreadDeath, so it only runs cleanup code when threads are stopped, not when they die for other reasons or run cleanly. If cleanup is important regardless of how the thread ends, you should put it in a finally clause, as discussed in the previous chapter.

We must point out once again, though, that the recommendation is not to use the stop method, but rather to iterate through each thread in the thread group and set its instance variable to make it voluntarily stop. We wish they would have added a method signature in the Runnable interface for this voluntary stop idea, but it would have affected too much existing code. You will see later in this chapter how to iterate through the threads in a thread group.


Ending Programs With Java Running Threads

You might be wondering at this point what happens when the main method ends and threads are still running. You saw that, after spawning the threads, the main method regains control immediately. The threads then starts running asynchronously in the background. What happens when the end of the main method is reached, and there are still background threads running?

When execution reaches the very end of main, the Java Virtual Machine queues up its exit until all active threads have finished running. That is, the program will remain running until all background threads have finished. You will notice this because you will not get control back at the command line where you issued "Java Xxx," and you will see Java listed as one of the programs still running in the call stack.

There are times when you simply want to force an exit. This might involve killing any rogue threads still running. You can do this by exiting your program with System. exit(0);. Unlike an implicit or explicit return statement, this does not wait for running threads to end. Sometimes, this is necessary for idle background threads.

Daemon Threads

The statement about programs not ending until all threads have finished has a corollary. When you create a Thread object, you can invoke the setDaemon(true) method on it to identify this thread as a daemon. What does that mean? You've sold your soul! Well, independent of that, it means this thread is a "service thread" that never ends. At program end time, Java will not wait for daemon threads before exiting. Instead, it will just kill those threads.

An example of a daemon thread is a timer, which just runs in the background and sends out "tick" events, say. Another example might be a thread that watches a data queue or telephone line. Marking these types of threads as daemons saves you the trouble of explicitly killing them yourself when you are ready to exit your program.

Java's Own Threads

Even if you do not use threads yourself, Java considers your non-threaded code to be part of a main thread. There are other default threads in any Java program as well, notably the garbage collector thread. This is a daemon thread that always lurks in the background, waiting for an opportunity to vacuum up an unused object. Using a graphical user interface causes another non-daemon thread to run, to watch for user events like mouse movements.


Thinking Asynchronous-Danger Lurks

In the examples of threads so far, you have used a thread to allow long-running code to be interrupted. In a real-world application, you will use threads in other ways. For example, you will use them for any potentially long-running operation to ensure overall system efficiency and higher throughput. Just as a bank has multiple tellers and a grocery store has multiple checkout counters, your programs will often have multiple asynchronous transaction threads.

This design often involves one common repository class, such as a bank or store class, and a separate transaction class that is threaded. You spawn multiple transaction threads, each taking as input an object in the repository, and acting on that object. The transaction thread class takes an instance of the repository class as a constructor parameter, and its run method invokes one or more of the methods on that repository object. This is illustrated in Figure 11.5.

Click To expand
Figure 11.5: A single repository class and threaded transaction classes.

In this design, you end up with many simultaneous threads using a single instance of an object. The implication is that they will be attempting to view and change the variables in that object simultaneously.

Consider a compiled COBOL program (module) that is bound into an OS/390 executable load module or OS/400 *PGM object. It has global data items, just as a Java class has instance variables. Like threads, you can have multiple users running your program simultaneously. They each get their own copies of those global variables. With threads, they all share the same copy! This can be dangerous, of course. The threads might step on each other-one changing a variable "under the foot" of another.

As an application programmer, you are used to this. You already have to deal with the problems of simultaneous access to your database, and you use locking mechanisms to ensure the integrity of the database. Thus, to build "thread-safe" Java programs, you have to learn the Java syntax and idioms necessary to do with instance variables what you already do with database records.

There are two more points to discuss before moving on to thread safety: the role of threads in the design of a complex Java multiple-user application, and how single objects can have multiple threads of execution.

You might not have to worry about complex, multithreaded applications if all you are doing initially is adding a Java graphical user interface to your host COBOL application. In this case, your Java user interface will run on the client, and each concurrent user will invoke independent instances (jobs) of your COBOL back-end code as needed. Your existing database logic in the COBOL code will be as robust as always.

As you delve deeper into writing and running Java server code, however, you might come to a design like the one shown in Figure 11.6.

Click To expand
Figure 11.6: A Java server with multiple clients.

In this scenario, rather than having separate jobs servicing each client, you have but one server job running, with one or more threads per client. This scales better because threads have less overhead than separate jobs (although, admittedly, both OS/390 and OS/400 do an exceptional job of handling many thousands of jobs). Combined with RMI (Remote Method Invocation), CORBA (Common Object Request Broker Architecture), or Servlets, this can offer an effective new way to design large-scale applications with thousands of concurrent users. To do this, you have to delve deep into threads and thread safety.

Regarding the second issue, how single objects can have multiple threads of execution, you might be a little unclear as to how this can happen. Think of a bank object. At any time, it might have thousands of individual threads, all calling its transferFunds method. It might seem confusing to have so many executing threads, perhaps all on the same method. Do not mix up objects with executing threads. One is about memory, and the other is about instruction pointers.

It might help to think of the object as a database file, and the methods as COBOL programs that use that database. You have only one database file, but, at any time, you have many users running the COBOL program or programs that manipulate that database.

Multi-Threaded Danger-An Inventory Example

The following example shows how multiple threads using a shared object can be dangerous. Consider a system where orders of an item are accepted and fulfilled. The in-stock inventory of the item is also monitored.

You might have a class called Inventory that manages this, as shown in Listing 11.8.

Listing 11.8: An Inventory class that takes orders and adjusts the inventory when stock is low.
Start example
public class Inventory
{
   private static final int AMOUNT_INCREMENT = 2000;
   private int onHand = 5000; // amount in inventory
   public boolean stop = false; // stop whole thing
   // method to fulfill an order
   public void takeOrder(int howMany)
   {
      int old = onHand; String error = "";
      if (stop) // have we been stopped?
         return; // exit now
      if (howMany > onHand)
         {
         // increase inventory
         addToInventory(howMany);
         error = "Order: " + howMany + ", old: " + old + ", new: " + onHand;
         }
         // actually take order
         onHand = onHand-howMany;
         // inventory should never be negative, but still...
         if (onHand < 0)
            {
            System.out.println("Error-onHand less than zero! " + onHand);
            System.out.println(error);
            stop = true;
            }
         } // end takeOrder method
       // method to increase inventory stock, taking into
   // account the size of the current order
   private void addToInventory(int howMany)
   {
      if (howMany > AMOUNT_INCREMENT)
         onHand +=(howMany-onHand)+1;
      else
         onHand += AMOUNT_INCREMENT;
   } // end addToInventory method
} // end Inventory class
End example

This class starts with an initial inventory amount of 5000 (onHand). Each order taken (using the takeOrder method) decrements the amount of the order from the inventory. First, a check is made to ensure the size of the order will not deplete the current inventory. If this would be the case, the inventory is increased before the order is filled (using the addToInventory method). Note that the stop instance variable is checked before even bothering to enter the body of the method. You will see where stop is set at the end of the method.

This is very basic stuff. What could go wrong? Look at the takeOrder method. Because the inventory is bumped up to cover the current order whenever necessary (admittedly, a non-robust algorithm), it seems ludicrous to have the if (onHand < 0) check. How can it get below zero if the lines of code just above it ensure that it does not? In a synchronized world, of course, it cannot. In a threaded world, though, it can. To see this, you need another class-a thread class-whose run method calls the takeOrder method on an instance of Inventory. This is a typical transaction type of thread class, as shown in Listing 11.9.

Listing 11.9: A typical transaction thread class.
Start example
public class OrderThread implements Runnable
{
   Inventory inventoryObject; // passed in to us
   int       howMany; // how many items to order
   // constructor
   public OrderThread(Inventory inventoryObject, int howMany)
   {
      this.inventoryObject = inventoryObject;
      this.howMany = howMany;
   }
   // "run" method, called by using Start()
// This method places the order for the given amount
   public void run()
   {
      // place the order
      inventoryObject.takeOrder(howMany);
   } // end run() method
} // end OrderThread class
End example

An instance of this class is created for every order, and it is run as a thread. There is only a single instance of the Inventory class. (In OO terms, this is called a singleton.) That instance is passed in via the constructor to every instance of the OrderThread class. This makes sense-while you get many orders, there should never be more than one inventory.

To test this little system, a final class is needed that contains the main method to get control from the command line. This method, shown in Listing 11.10, creates a single Inventory object, but many sample OrderThread objects to really stress-test the system.

Listing 11.10: A class to test the previous example.
Start example
public class TestInventory
{
   public static void main(String[] args) // cmdline entry
   {
      Inventory inv = new Inventory();
      java.util.Random random = new java.util.Random();
      int idx;
      System.out.println("Running... ");
      for (idx = 0; (idx <= 1000) && !inv.stop; idx++)
      {
         int nextRandom = java.lang.Math.abs(random.nextInt());
         nextRandom = (nextRandom % 10000) + 1;
         OrderThread newOrder = new OrderThread(inv, nextRandom);
         Thread newThread = new Thread(newOrder);
         newThread.start();
      }
      if (inv.stop)
         System.out.println("...stopped at: " + idx);
      else
         System.out.println("...all orders placed.");
   } // end main method
} // end TestInventory class
End example

This test creates 1,000 order-taking threads, each one asking for a random number of items that ranges up to 10,000. Potentially, all of these threads will run simultaneously, really testing the logic that is designed to never let the inventory fall below zero. This code creates a single instance of the Inventory class and passes it into every instance of OrderThread, so all threads are operating on a single object.

A Random object is used from the java.util package to generate random numbers for the simulated orders. If the inventory ever does fall below zero (seemingly impossible, but…), we notice this and stop creating new threads because the system has obviously degenerated and is now unstable.

If you compile and run these classes, you should get output similar to the following:

>java TestInventory
Running...
Error-onHand less than zero! -921
Order: 5820, old: 2001, new: 4899
Error-onHand less than zero! -1695
Error-onHand less than zero! -7353
Order: 5658, old: -921, new: -1695
Error-onHand less than zero! -7582
Order: 229, old: 1, new: 2001
...stopped at: 104
Order: 7354, old: 5821, new: 5659

Your actual output might be different because it is timing-dependent. There are a number of very interesting (scary?) things about this output:

  • The inventory fell below zero despite explicit code to check and prevent it.

  • Even after the stop variable was set to stop new threads from executing takeOrder, numerous error outputs were printed, indicating these threads were already past the if (stop) return; code at the beginning of the method.

  • The Error and Order lines are not always synchronized in the output, even though these were issued one line after another in the code.

  • The Error and Order lines both print out the value of the onHand variable (look at the "new" value for the Order lines), just at different points in the takeOrder method. While no changes are made to the variable in the intervening code, the variable value has still changed between the two.

All of this clearly indicates one thing: computers cannot be trusted! Well, actually, it demonstrates that there are multiple threads of execution running inside the takeOrder method simultaneously. The switch from one thread to another happens quickly (from one line to the next) and arbitrarily. This causes a problem because of the common variable (onHand) these lines of code are sharing and manipulating.

What is happening? Between the line of code that checks the onHand balance (if (howMany > onHand)) and the line of code that decrements the balance (onHand = onHand-howMany;), another thread gains control and runs the same decrementing line of code. The check is passing for a particular thread, but by the time it actually does the onHand-variable decrement, another thread has already decremented the variable, as shown in Figure 11.7. This causes the variable to be decremented twice without the check, letting it go below zero.

Click To expand
Figure 11.7: Thread time-splicing.

Threads work by preemptive time-slicing-that is, a thread is given a small amount of CPU time to perform a few atomic instructions. Then, it is preempted, and another thread is given a similar amount of CPU time to perform a few of its instructions. This continues until each thread is complete (by reaching the end of the run method).

An atomic instruction is essentially one bytecode instruction. Generally, a single line of Java source code compiles into numerous Java bytecode instructions. This is not unlike COBOL, where a single statement can compile into multiple underlying assembly or machine-code instructions. This means you cannot guarantee even that an entire line of source code will run before the next thread is given control.

Solution: Synchronizing Asynchronous Execution

This might sound hopeless. If you cannot guarantee the order of execution, how can you possibly guard against unexpected concurrency errors? The answer to providing this thread-safety is elegantly simple. It involves merely adding one Java keyword to one line of code!

You need to be able to guard against this unexpected interruption whenever you have code that depends on a common variable remaining stable from one line of code to the next. The magic keyword in Java to do this is synchronized. When specified as a method modifier, it tells Java that this entire method needs to be executed without interruption by other threads. Effectively, it allows only one thread to execute this method at a time. All waiting threads get queued up at the door. As each thread finishes executing the method, the next thread that is waiting is let in.

Let's change the takeOrder method definition to include the keyword synchronized:

public synchronized void takeOrder(int howMany)

Now, after compiling, you should be able to run the test again without any unexpected errors:

>java TestInventory
Running...
...all orders placed.

This code will run more slowly because you have reduced the amount of asynchronous execution considerably. However, it will run correctly, and that, after all, is the fundamental requirement.

Fine-Grained Synchronization

The entire takeOrder method has been synchronized, but you could actually synchronize just the lines of code you need to guard, instead. Java defines a synchronized block as a block of code that can be placed inside a synchronized statement so only that block is protected from interruptions by other threads. To use this fine-grained synchronizing, you have to think carefully about what code is exposed by multiple concurrent threads of execution. At a minimum, it is any code that changes a common instance variable. If you have code that tests the current value of the variable and then does work based on that current variable value, you will need to synchronize the entire block. You need to always be thinking "what if the value of the variable changed right now" for each line of code.

In the case of our example, the onHand variable check and the onHand variable decrement need to be treated as a single unit of operation to guarantee that no other thread can decrement the variable in-between and cause an underflow.

So, synchronized is removed from the takeOrder method declaration and instead placed around this sensitive block of code:

synchronized(this)
{
  if (howMany > onHand)
    {
      // increase inventory
      addToInventory(howMany);
      error = "Order: " + howMany + ", old: " + old + ", new: " + onHand;
    }
  // actually take order
  onHand = onHand-howMany;
} // end synchronized(this)

In this case, there will be no appreciable difference in total execution time. This is true only because almost the entire method's code ends up in the synchronized statement, anyway. If there were a significant amount of other code outside of the synchronized block, you would see overall throughput improvements.

In general, you should simply use synchronized at the method level (as a modifier) on any methods that manipulate common variables, except in the following cases:

  • You are very sure about the minimum set of code that needs to be synchronized. In this example, moving the line that decrements onHand out of the synchronized block causes the unexpected error situations to happen again. If you were not checking for this in the code, you would end up with a very serious bug that could go undetected for a long time, or until suppliers receive a negative-amount order!

  • The synchronized versus unsynchronized code ratio is worth the extra risk.

Was it even worth using threads in this example? Maybe not. We ended up having to synchronize the majority of the code. However, threads are usually a good idea because your code per transaction is usually complex, and the synchronized part-even if it is an entire method-is relatively small. That is, the thread will usually involve more code than a single method call.

The use of threads can give very busy applications at least the chance for individual transactions to be completed in a shorter time than if they all had to wait for the previously submitted transactions to complete. Further, by spawning threads, you give control back to the end user immediately, rather than forcing him or her to wait an indefinite amount of time for the transaction to complete. This reason alone dictates that threads should be used more often than not for user-initiated transactions. "Leave the customer in control" is a maxim to live and code by.

Threaded Application Design Alternatives

Each transaction in the previous example was put in its own thread. This is not the only possible design, of course. Another option would be to give each user a thread instead, and let it perform the transactions synchronously within the thread. This is a reasonable alternative because users will expect their own transactions to be performed in the order they are submitted, anyway. This option might, thus, reduce the overall number of threads running, and improve response time.

If you have too many threads competing for processor time, you might run into thrashing, a situation where so many threads are running that each one gets only enough time to do a minuscule amount of work each slice. More effort is spent switching between the threads then is spent running the threads.

Another option would be to create a fixed-size thread-pool of service threads. In this design, a predetermined number of threads, say a dozen or so, are spawned at application start time, and each transaction or thread-qualifying request is fed to the next available thread. If no thread is available, the request is queued up, and the next transaction thread to become available reads it from the queue and executes it. Or, the thread-pool grows by one thread to a pre-set maximum. This can reduce thread-switching and improve performance for heavy-use applications.


More On Synchronized Threads

You have now seen the basics of threads in Java. The remainder of this chapter goes into more detail, and can be safely skipped if you are only looking for an introduction to Java or to threads. If you are ready for more detailed information on threads, though, read on.

The synchronized keyword, as you have seen, can be specified as a method modifier or as a code-block keyword. In the latter case, you saw in the example that it requires a parameter. For the example we specified, the keyword this represented the current object.

The synchronized keyword locks an object so that you have exclusive access to it. It always locks some class instance-a Java object. When used as a method modifier, it locks the object the method is part of. When used as a code-block keyword, it locks the object you specify as a parameter. In both cases, the entire object is locked, not just the method or code block.

Thus, at runtime, when a thread (including the main thread) calls a synchronized method or tries to enter a synchronized block, the algorithm is this:

  • Is the specified object locked? (Is another thread running any synchronized code for this object right now?)

    • Yes-Wait in this object's queue.

    • No-Lock this object, and run the code.

  • When the code is done running (execution reaches the end of the method or block) the object is unlocked and the next thread in the queue is allowed in. Nested synchronized methods, or blocks on the same object, bump up the lock count for the object. It is not until the current thread that has the lock reduces the lock to zero that the object is finally unlocked for others to use, as shown in Figure 11.8.

    Figure 11.8: A synchronized lock count.

The use of synchronized as a method modifier is actually equivalent to putting the entire method body in a synchronized(this) block. It is, to be sure, the safest and easiest way to synchronize sensitive code that changes common variables in this object. There will also be times when code in one method changes variables in another object (either directly or through setXXX methods). In these cases, you have to use synchro-nized(object) blocks around the sensitive code, where (object) is the object reference variable that will be changed.

When you lock an object via the use of synchronized (either as a method modifier or a code-block keyword), it is important to know that you do not block other unsynchronized methods from running in that same object. You can have one thread running an unsynchronized method that reads the common variables at the same time another thread is running inside a synchronized method that perhaps changes those variables. Locking an object only affects other threads that attempt to run synchronized code on that object.

Normally, code that reads only a common variable can be left unsynchronized, unless it involves multiple lines of code that depend on the variable not changing from one line to the next. For example, suppose you have something like this:

if (account < 0)
sendNotice("You have an outstanding account of " + account);

You will clearly have to be careful that the account value does not go above zero by the time the sendNotice method is called in the second line. These two lines should be placed inside a synchronized(this) block to ensure the common variable does not change from one line to the next.

Synchronizing Producer/Consumer Relationships

There are times when you will have one synchronized thread that needs to wait on another thread. Java supplies two methods, each one part of the base java.lang.Object class, which are available to all. They are called wait and notifyAll. These methods can only be used inside synchronized methods or blocks. The wait method waits either indefinitely or for a specified number of milliseconds, until another thread calls notifyAll. This method is always used inside a loop that is checking for some condition the thread is dependent on. After waiting, the condition is rechecked:

while (variable < threshold)
     wait();

The thread that calls wait gets put on a wait queue for the current object, until the object is unlocked. That allows another thread to get in for this object. The threads on a wait queue are only released when notifyAll is called by some other thread for this same object. There is actually a method called notify as well, which explicitly releases the thread that has been waiting the longest, as opposed to notifyAll, which releases all waiting threads.

What does it mean to be released? It means this thread is back in the queue, waiting to get into the synchronized object. Another thread might have gotten in, because wait resets the lock count to zero in the meantime. When it does finally get back in, it starts executing again where it left off at the wait method call. The lock count is then restored to the value it had when the thread call originally called wait. This is illustrated in Figure 11.9.

Click To expand
Figure 11.9: A lock queue versus a wait queue. Tn represents thread n.

In Figure 11.9, Tn represents thread n. If there were more threads in the wait queue, notifyAll would move them all to the lock queue, while notify would move only the first one (T1, in this case). Note that a thread will also move from the wait queue to the lock queue if it specified a number of milliseconds in the call to wait(mmmm), and that time limit has expired.

You will use the wait/notify pair in situations where there is one section of code that produces some output that another section of code (perhaps the same method, perhaps a different method) is dependent on. For example, you might have a queue object with synchronized methods for reading and writing the queue. The read method would wait until the queue is not empty:

// inside read method
while (isEmpty())
    wait();

The write method would notify or notifyAll after adding the entry to the queue:

// inside write method
addEntry(newItem);
notify();

When should you use notify, and when should you use notifyAll? Good question. If only there was a good answer. In this example, only one item is added to the queue. Because only one thread will be able to use it, using notifyAll is not appropriate. If there was an append method that added numerous items to the queue, notifyAll would be used so all waiting threads would get a chance. The worst that will happen is that one or more threads will return to life, only to find their conditions are still not met. They will redo their wait calls.

A thread calling the read method in this case would be a good candidate for a daemon thread. You would typically not want the application to be prevented from exiting if that read method was still waiting for an entry on the queue.

Deadly Embrace

Synchronization is a great tool to ensure correctness in your multithreaded applications, but it is dangerous tool, as well. You can very easily arrive at a situation where all running threads are blocked because they are waiting for each other. This is like having a law that states, "When two trains meet at an intersection, neither can leave until the other is gone."

Consider the situation, shown in Figure 11.10, in which thread T1 runs synchronized method obj1.method1(), locking obj1. Thread T2 runs synchronized method obj2.method2(), locking obj2. Now, obj1.method1 tries to call obj2.method2, and so thread T1 is put in the lock queue for obj2 while it waits for thread T2 to finish. But obj2.method2 calls obj1.method1, and so thread T2 gets put in the lock queue for obj1 while it waits for thread T1 to finish. Each is locked now, waiting on the other, and will wait forever. No other threads needing these objects will ever run, either. Also, unless these are daemon threads or System.exit is used, the program will not end unless it is forcefully killed.

Click To expand
Figure 11.10: Deadlock.

This is a deadly embrace known as deadlock. There is nothing that Java can do to help you here. If you hit this problem, it will manifest itself intermittently (it is timing-dependent) and be a complete bear to debug. You need to avoid the problem outright by careful design. That is, by avoiding mutual calls from one object's synchronized method to another's, and back again. If necessary, use a third object with a synchronized method that makes the necessary calls to the other two objects via unsynchronized calls.

The wait and notify methods do not help or hinder the deadlock possibilities, but they do add the risk of a thread waiting forever if another thread does not someday notify it. However, because the waiting thread unlocks the object, at least other threads have a chance to run. If it is important for the waiting thread to eventually run (say, if it is waiting on resources in order to place a customer order), this will still be a serious problem. You might want to specify a time-out limit, even if it is ten minutes, on the wait method to indicate when something appears to be stuck. Of course, there is a risk that it is waiting on itself:

public synchronized void waitOnMe()
{
   while (variable < threshHold)
      wait();
   variable += threshHold;
   notify();
}

In this case, the code that notifies waiting threads is clearly unreachable. There is no point in waiting on a situation your subsequent code will address. Just go ahead and address it.


Thread Priorities

The diagrams and discussion about synchronization in this chapter use the terms "lock queue" and "wait queue." They are misnomers, however, in that they imply that threads are put into and taken off the queues in a deterministic manner-say, first in, first out. This is not the case. They are randomly chosen by the thread scheduler. The algorithm used to choose them is not programmatically predictable. It might vary from platform to platform, depending on the underlying operating system's scheduling support for threads. Better terms might be "lock set" and "wait set," but these create their own confusion.

One of the criteria Java tries to enforce in the scheduling of threads (and that all multithreaded operating systems support) is thread priority. By default, your threads all have normal priority and, thus, the same relative weight for this criteria. By using the Thread method setPriority, you can set the priority to any number between one (lowest) and ten (highest). The default is five (normal). For convenience, there are predefined constants in the Thread class for MIN_PRIORITY (1), NORM_PRIORITY (5), and MAX_PRIORITY(10).

New threads inherit the priority of their parent threads (the main thread has normal priority), or that of their ThreadGroup, if one is specified. Using ThreadGroups is a convenient way to set the priorities of all threads with similar roles.

When the thread scheduler needs to pick another thread for its turn to run, get off the locked queue, or get off the wait queue, it uses an algorithm involving both the thread's priority and its time waiting so far. Higher-priority threads, all things being equal, will get more CPU time than lower-priority threads. It is a general rule of thumb that user-interface threads run as a high priority to improve response time, and daemon background threads run as a low priority to take whatever cycles they can steal.


Loose Threads

We conclude this chapter by mentioning some other aspects and functions available to you, as a threaded programmer:

  • Named threads. When you create a new Thread object, you can specify a new unique name for that thread, and query it at any time. This can be helpful for debugging or logging purposes.

  • Yield. This is a friendly static method in Thread that you can use in your code to voluntarily give up your current CPU slice, and let another thread run. If all your threads consist of the same synchronized method, there is no point to invoking yield in that method because no other thread can run anyway. Until you finish the method and, hence, relinquish the lock, the other threads remain stuck on the lock queue. Unlike wait, yield does not unlock the object or put your thread on the wait queue.

  • Suspend and resume. One thread can suspend another thread by calling its suspend method (good choice of method name, no?), and subsequently bring it back to life by invoking its resume method. This could be useful when you want to confirm a user's cancel request, for example. While asking the user to confirm, you would suspend the thread (or ThreadGroup). If the user decided not to cancel after all, you would resume it. These methods also make a "Pause" button easy to implement.

  • Join. This is an interesting method that makes it easy to wait for a thread to finish. You simply call join on its Thread object. This will not return until that thread has finished (by returning from its run method). An example of where this is very useful follows shortly.

  • Checking the pulse. You might occasionally need to determine if a given Thread object has finished running or not. The Thread method isAlive will return true if the thread has not yet finished (not returned from its run method). Be careful, though. If you have not yet called start on this thread, you will get false from this query.


Timing Threaded Applications

When writing applications, you will often want to measure the elapsed time to gauge the performance impacts of changes you make. This is especially true in a multiple-thread application, where you want to ensure that adding a wait here, a yield there, and a synchronized statement over there does not seriously degrade the overall throughput of the application.

To measure an application's time, an easy trick is to change the main entry point to record the current time in milliseconds at the very beginning, and record it again at the very end. Then, take the difference and print it to standard-out (that is, the console) or a log file. You then make a number of test runs through the application and average the total elapsed time. Now, if you change something, you can rerun the test cases and compare the average elapsed time. Of course, this will be very dependent on the current load of the system, so it has to be taken with a grain of salt. For code running on the workstation, the usual trick is to run the test cases after a fresh reboot, so memory is in as consistent a state as possible between runs.

To measure the elapsed time in the inventory example, change the main method in the TestInventory class to print out the elapsed time in milliseconds that the entire process takes. Use the Date class in the java.util package to record the current time in milliseconds twice-once before all the threads are spawned, and once after, and display the difference, as in Listing 11.11.

Listing 11.11: The TestInventory class to print out the elapsed time.
Start example
// Mainline
public static void main(String[] args) // cmdline entry
{
   Inventory inv = new Inventory();
   java.util.Random random = new java.util.Random();
   int idx;
   java.util.Date startTime = new java.util.Date();
   System.out.println("Running... ");
   for (idx = 0; (idx <= 1000) && !inv.stop; idx++)
   {
      int nextRandom = java.lang.Math.abs(random.nextInt());
      nextRandom = (nextRandom % 10000) + 1;
      OrderThread newOrder = new OrderThread(inv, nextRandom);
      Thread newThread = new Thread(newOrder);
      newThread.start();
   }
   if (inv.stop)
      System.out.println("...stopped at: " + idx);
   else
      System.out.println("...all orders placed.");
   java.util.Date endTime = new java.util.Date();
   long elapsed = endTime.getTime()-startTime.getTime();
   System.out.println("Elapsed time: " + elapsed);
} // end main method
End example

Actually, this is not quite the correct way to do this. Why? Because when you get the current time at the end of the main method, you cannot be sure the threads have finished running. Remember, the start calls return immediately, so you will be taking the second time-reading potentially before the threads have even started to execute.

You need a way to wait for all the spawned threads to complete, and then check the time. You can do this by creating all the threads in a single ThreadGroup, and then waiting for them all to finish. This is a good strategy anyway, because it also provides an easy way to stop all these threads when something untoward happens or is detected. Just use ob-ject.stop() on the ThreadGroup object. (You should really use the voluntary stop method, but for brevity, we leave that to you.)

How do you wait for all the threads in the group to finish? Painfully, as it turns out! You have to enumerate (list) all the threads in the group, then invoke the Thread method join on each of them. The join method waits for the thread to finish, and only then do you get control back. (If it is already finished, you get control back immediately.) There is no join method for the ThreadGroup itself, but the code is not that intellectually taxing, so you can add it before the final time-reading is taken. Note that join might throw an InterruptedException exception, so you have to catch it, as in Listing 11.12.

Listing 11.12: How to wait for multiple threads to complete.
Start example
// Mainline
public static void main(String[] args) // cmdline entry
{
   Inventory inv = new Inventory();
   java.util.Random random = new java.util.Random();
   int idx;
   java.util.Date startTime = new java.util.Date();
   ThreadGroup orderGroup = new ThreadGroup("Orders");
   System.out.println("Running... ");
   for (idx = 0; (idx <= 1000) && !inv.stop; idx++)
   {
      int nextRandom = java.lang.Math.abs(random.nextInt());
      nextRandom = (nextRandom % 10000) + 1;
      OrderThread newOrder = new OrderThread(inv, nextRandom);
      Thread newThread = new Thread(orderGroup, newOrder);
      newThread.start();
   }
   if (inv.stop)
      System.out.println("...stopped at: " + idx);
   else
      System.out.println("...all orders placed.");
   // wait for all threads to end...
   Thread allThreads[] = new Thread[orderGroup.activeCount() + 10];
   orderGroup.enumerate(allThreads); // populate the array
   for (int j = 0; j < allThreads.length; j++)
   {
      if (allThreads[j] != null)
      try
      {
         allThreads[j].join();
      }
      catch (InterruptedException exc) {}
   } // end for-loop
   java.util.Date endTime = new java.util.Date();
   long elapsed = endTime.getTime()-startTime.getTime();
   System.out.println("Elapsed time: " + elapsed);
} // end main method
End example

Now, with accurate elapsed-time checking in place, make a few sample runs and record the average time:

>java TestInventory3
Running...
...all orders placed.
Elapsed time: 6210
>java TestInventory3
Running...
...all orders placed.
Elapsed time: 6150
>java TestInventory3
Running...
...all orders placed.
Elapsed time: 7090

In this case, the average time is 6483 milliseconds. (Your mileage may vary.)

Note that the TestInventory class can be kept as a separate test case, which you can easily rerun without affecting your real application code. You can write multiples of these little main test classes to test various inputs, outputs, and timings. By keeping these as separate classes, you do not have to worry about shipping them with the production application.


Summary

This chapter covered the topic of threads in Java:

  • The two methods for defining threads: extends Thread and implements Runnable.

  • The ThreadGroup class for grouping threads with similar functions.

  • The use of the Thread class's stop method to stop threads or entire thread groups.

  • The use of a voluntary check of an instance variable to stop a thread.

  • The definition of a daemon background thread.

  • How to create synchronized methods and code to avoid common variable corruption.

  • How to use Thread's wait and notify methods as a means of waiting and triggering codependent synchronized threads.

  • The definition of deadlock and its potential to happen in synchronized threads.

  • The concept of thread priorities, as set by Thread's setPriority method, and its effect on thread CPU share.

  • The use of the Thread class methods sleep, yield, suspend, resume, and isAlive for managing non-synchronized thread states.

  • The use of the Thread class method join to wait for a running thread to finish.


Chapter 12: User Interface

Overview

All of the Java examples in the preceding chapters involve user input or output via the command line, with System.in to read from the console and System.out to write to the console. This, of course, is not the real world of application programming in this fresh new century. Traditionally, though, programmers have written "green-screen" user interfaces to COBOL programs on OS/390 and OS/400.

On OS/390, there are a number of options for screen definitions, such as ISPF panels, CICS BMS maps, IMS MFS maps, third-party products, and so on. On OS/400, this is done using display-file DDS and tools such as Screen Design Aid (SDA) or the CODE Designer tool in CODE/400. (Check it out at www.ibm.com/software/ad/varpg.) An OS/400 COBOL program interacts with these by defining a WORKSTN transaction file, and using format 2 of the COPY statement to copy in the buffer definitions for the display-file record formats.

Because of the various options for green-screen development, and because you are intimately familiar with these already, this chapter does not dwell on the COBOL programming model. Instead, it focuses on how to write interactive programs in Java. Java programs use graphical user interfaces (GUIs) to present and query information to and from the user. (Because these are actually object-oriented user interfaces, you might say they are OOUI GUI!)


User Interfaces In Java: Options, Options, Options

In Java, you have at least three options for your user interface:

  • Abstract Windowing Toolkit (AWT). This is the original set of GUI packages that are used, programmatically (versus externally described), to create and display a GUI in Java. Web browsers have supported this since 1996.

  • Swing. This is the follow-on set of packages to AWT, for creating and displaying a GUI in Java. It is much richer in function than AWT, and therefore more complex to learn and use. It is worth it, though.

  • Servlets, JavaServer Pages, and HTML. These are not collections of Java GUI classes like AWT and Swing. A Servlet is a Java class that runs on the server, and is called by a Web browser from an HTML form the same way a CGI program is called. When called, the Servlet's job is to process the input from the form and create, on the fly, a new HTML page that is sent back to the browser and displayed there. To separate the HTML from your Java Servlet code, you can use JavaServer Pages (JSPs), which are HTML files with embedded Java and holes for dynamic data that a Servlet supplies in the form of a Java Bean. So, for this option, Java is not used for the GUI, but rather to generate the dynamic HTML UI. Chapter 14 provides more information on Servlets and JSPs.

So, what will you use? It depends! You can use AWT or Swing to write traditional client applications that will run on any client. You can also use AWT or Swing to write applets that are embedded in a Web page and downloaded on demand with the page. Alternatively, you can use JSPs or Servlets to write code that produces HTML user interfaces.

Swing and AWT (in that order) offer the most robust user-interface options. If a public Internet presence is your goal, however, you might find the applets created are too big to be downloaded quickly on slower modems. As Internet bandwidth marches on, this might become less of a concern. In the meantime, if you really want fast, publicly accessible Internet user interfaces, you should use Servlets. If they write out standard HTML, any browser can use them, and they'll be efficient. But HTML is limiting in its support for user interfaces, so you might find yourself producing HTML that also contains a few small applets to augment the sparse HTML functionality. HTML is getting better, and a new generation is coming (see www.w3c.org).

You can't learn all of these things in one chapter in one book, so this chapter concentrates on AWT, and touches on Swing. Almost all of the skills you learn in AWT are transferable to Swing, which is a true superset of the AWT functionality. Furthermore, even if you ultimately decide to use Servlets, you might want to supplement the HTML with applets written in AWT (or Swing), so it is good to know. In any case, the examples and discussion here will help solidify your growing knowledge of Java. And if your first Java application is a little productivity tool you've always wanted, this is the place to start.

Java GUIs

Java AWT and Swing have no externally described language for defining the user interface independent of your code. This is in contrast to OS/390's maps and OS/400's display files. Rather, the user interface is built up dynamically with Java code, as you will see. This programmatic approach to the user interface offers some advantages (for example, use of conditional logic versus hard-to-maintain indicator conditions), but also some disadvantages relative to reuse and code size. The reuse disadvantages can easily be overcome through the use of good object-oriented principles. For example, if you have common window and dialog styles, and common window parts such as a customer number prompt, these can be encapsulated in classes that can be easily reused and extended as needed, leading to code savings and consistent standards.

There are a number of fundamental differences, shown in Table 12.1, between the green-screen programming model and Java's dynamic, event-driven programming model for user interfaces. Each of the differences in Table 12.1 is covered in this chapter. (In the table, modal means the program blocks waiting on input, while modeless means other windows are still active and accessible.)

Table 12.1: Green Screen Versus GUI Differences.
Green Screen | Java
Enter or function-key driven. | Event-driven input.
Modal display of records. | Modeless display of windows.
24-by-80 screen size. | Programmer-defined screen sizes, by pixels.
Row, column field addressing. | Relative field addressing.
Attributes set externally. | Attributes queried and set via method calls.
Support for online help. | No built-in support for help. You code it yourself, using, say, HTML help.

What About WYSIWYG Tools?

There are lots of Java WYSIWYG design tools that provide drag-and-drop ease to lay out your design, such as the Visual Composition Editor in IBM's VisualAge for Java. These design tools subsequently generate the Java code for you. The use of these tools is, of course, encouraged. This chapter, however, teaches you the hard way. We believe that even with great GUI tools, you are always a better programmer for having an understanding of the underlying code.

AN OVERVIEW OF JAVA AWT

The basis of Java's user interface support is the classes in the java.awt package. (Again, AWT stands for Abstract Windowing Toolkit.) These are a hierarchy of classes that all extend java.awt.Component. The classes can be categorized as follows:

  • Basic components, such as Button and Checkbox, that implement the individual GUI parts you would see in a typical GUI application.

  • Containers, such as a Frame, Dialog, and Panel, that contain basic components.

  • Layout managers, which define how components are to be laid out in the container, relative to each other.

  • Events, from the java.awt.event package, used to process input from the components.

The idea is to create an instance of a container object, such as a Frame window, and add to it instances of the basic components, such as Checkbox objects. This is done using the add method of the container object. Let's quickly look at an example, in Listing 12.1.

Listing 12.1: Using the add method of the frame class.
Start example
import java.awt.*;
import java.awt.event.*;

public class TestButton
{
   public static void main(String args[])
   {
      Frame window = new Frame(); // Main window
      Button okButton = new Button("OK"); // push button
      window.add(okButton);
      window.setVisible(true); // show it!
   }
}
End example

This will display a main window, shown in Figure 12.1, with an OK push button. (You have to size the window after it is displayed to see it like this.)

Figure 12.1: A simple window with a push button.

Any questions? More details follow. All of the following examples import the java.awt and java.awt.event packages, so you can easily access all the classes and interfaces within them, as shown here:

import java.awt.*;
import java.awt.event.*;

Basic Components: Buttons, Lists, Text, and More

Let's start by enumerating the basic components that Java supports, in Table 12.2.

Table 12.2: Basic AWT Components.
Component | Description
Button | Push buttons.
Checkbox | Check boxes (multiple selection) or radio buttons (single selection).
Choice | Drop-down list box.
Label | Text constant.
List | Selection list (single column).
TextField | Entry field (single line).
TextArea | Entry field (multiple line).

Table 12.3 is a quick summary of the typical way to instantiate and initialize these objects.

Table 12.3: How to Use the Basic AWT Components.

Instantiation

Parameters

button1 = new Button("Cancel");

Text for a push button. Use setLabel to change.

cbFish = new Checkbox("Fish",true);

Text for a check box, in its initial enabled state. Use setLabel and setState to change, respectively.

cbg = new CheckboxGroup();
rb1 = new Checkbox("Option 1", cbg, false);
rb2 = new Checkbox("Option 2", cbg, false);
cbg.setSelectedCheckbox(rb1);

For radio buttons (single-choice), first cre-ate a CheckboxGroup, then specify it when creating all radio buttons in the same group. Finally, use CheckboxGroup's setSelectedCheckbox method to identify the radio button to be preselected.

dropdown = new Choice();
dropdown.add("choice 1");
dropdown.add("choice 2");
dropdown.select(0);

For drop-down lists, create the drop-down and add items to it. Use select to choose the relative item number to be ini-tially selected.

prompt1 = new Label("Enter name");

Text. Use setText to change. Use setAlignment to set alignment to LEFT, CENTER, or RIGHT.

listbox = new List(2,false);
listbox.add("item 1");
listbox.add("item 2", 0);
listbox.select(0);

Number of items per page, and whether or not multiple selection is allowed. Items can be appended (add), inserted (insert), selected (select), and removed (remove).

entryName =
new TextField("yourname", 10);

Initial text and number of columns. Use setText and setColumns to change.

entryComments = new
TextArea("comments", 5, 1,
TextArea.SCROLLBARS_BOTH);

Initial text to display, number of rows and columns, and which scroll bars to show (_NONE, _BOTH, _HORIZONTAL_ONLY, OR _VERTICAL_ONLY).

Figure 12.2 shows what these components look like.

Click To expand
Figure 12.2: A quick overview of all AWT components.

All of these classes extended the Component class in the java.awt package, so they all pick up some important methods from this parent class, as shown in Table 12.4.

Table 12.4: Common Methods Shared by All AWT Components.
Description | Method
Enabling/disabling ("graying out"). | setEnabled(true/false), isEnabled()
Showing/hiding. | setVisible(true/false), isVisible()
Setting focus to this component. | requestFocus()
Setting focus to the next component. | transferFocus()
Setting background color. | setBackground(java.awt.Color)
Setting foreground (text) color. | setForeground(java.awt.Color)
Setting the font. | setFont(java.awt.Font)

Colors are defined as constants of the Color class (also in java.awt), as shown in Table 12.5.

Table 12.5: Basic AWT Color Constants in the Color Class of the java.awt Package.

Colors

Color.black

Color.blue

Color.cyan

Color.darkGray

Color.gray

Color.green

Color.lightGray

Color.magenta

Color.orange

Color.pink

Color.red

Color.white

Color.yellow

For example, to change a text constant's (java.awt.Label) colors, you might code the following:

Label constant = new Label("**overdraft**");
constant.setBackground(java.awt.Color.red);
constant.setForeground(java.awt.Color.white);

By default, the colors from the user-specified system setting are used. You can query these, as well, by using theSystemColor class in package java.awt, as shown in Table 12.6.

Table 12.6: Color Constants in the SystemColor Class of the java.awt Package.
System Color | Description
activeCaption, activeCaptionText, activeCaptionBorder | Background, text, and border colors for captions in window borders.
control, controlText, controlHighlight | Background, text, and highlight colors for components.
controlDkShadow, controlShadow | Dark shadow and regular shadow colors for components.
desktop | The color of the desktop background.
inactiveCaption, inactiveCaptionText, inactiveCaptionBorder | Background, text, and border colors for inactive captions.
info, infoText | Background and text colors for help text.
menu, menuText | Background and text colors for menus.
scrollbar | The background for scrollbars.
text, textText | Background and text colors for text components.
textHighlight, textHighlightText | Background and text colors for highlighted text.
textInactiveText | The text color for inactive text.
window, windowText, windowBorder | Background, text, and border colors for windows.

Because SystemColor extends Color, you can substitute these colors anywhere a Color is allowed. If you are artistically inclined, you can create your own unique colors by specifying the red-green-blue components on the constructor of a Color object, or by using the brighter and darker methods.

For fonts, you have to create a new Font object (also in java.awt) by specifying a font name, style, and size:

constant.setFont(new
   Font("sansSerif",java.awt.Font.BOLD,12));

See the Font class's documentation for more details on creating fonts.

These are the basic building-block classes that you will use repeatedly throughout your AWT programming.

Containers: Frames, Dialogs, and Panels

In order to see the basic components you created in the previous section, you need a window of some kind. This is usually an instance of the Frame class, which provides a main window with all the usual pieces—border, title bar, minimize, maximize, and close buttons, and a system menu in the upper left corner. You create a frame, or main window, easily enough:

Frame myWindow = new Frame("My First Java Window");

This creates an actual window. It is not too useful yet, because you cannot see it until you add this:

myWindow.setVisible(true);

Now it displays, as a tiny little thing. Prior to calling show, you need to do some typical tailoring:

  • Set the window's size. This is done using the setSize method, and specifying the width and height of the window in pixels (a very small graphical unit of measurement, representing a dot on the screen). Finding the exact size is a matter of trial and error, but try the following (after adding components to the window, you can use the pack method to resize the window to match the contents):

    myWindow.setSize(300,200);

  • Set the window's position. This is done using the setLocation method, specifying the x and y pixel address relative to the upper left corner of the screen (with the x pixels across, and the y pixels down). The address is of your window's upper left corner. Try this to start:

    myWindow.setLocation(200, 100);

  • Set the window's title. If this is not specified in the constructor to Frame, use setTitle.

  • Optionally, set the window's font, background color, and foreground color. These will be inherited by all the components you add to the window, but can be overridden by individual components. Use setFont, setBackground, and setForeground for these. By default, your system's current settings are used.

  • Finally, add all the components you wish to display in the client area or middle part of the window. We will cover this crucial step shortly.

Before discussing the first sample code, let's pause for a word on programming style. We suggest that, for each window in your application, you have a new class that extends the Frame class, and initializes and populates it in the constructor. Define all of the component and container objects as instance variables of the class, and instantiate them in the constructor. You will need these variables to be available to your other methods when it comes time to process input; hence the need to make them instance variables.

Why define the class to extend Frame? Good question. Recall from chapter 9 that the rule of thumb is to extend a class, rather than containing an instance variable of it, when your new class is a flavor of that class. In this case, the new class is a frame window, so you will extend the Frame window class. This gives all users of the class instant access to all of the methods inside the Frame class, too. Mind you, there are many who prefer to contain a Frame class object inside their class, since it leaves them free to extend another class. (In Java, you can only extend one class, remember.) It is up to you, but if you choose this route, you should include a getFrame method to return a reference to that variable, so callers can manipulate the Frame object directly.

At any rate, you might have a new class with code that looks like Listing 12.2. Note the use of super. You should always call the constructor of the class you extend, in this case Frame. This actually creates the frame window. Note also that the class is not public. This is, again, a style choice. We usually keep the default access of a package for window classes because only our code inside the package will be using this window. If you create a generically useful window that you want the programming public at large to be able to instantiate and use, make that class public.

Listing 12.2: A template for a window class In Java AWT. GUI initialization is done in the constructor.
Start example
import java.awt.*;
import java.awt.event.*;

class TestFrame extends Frame
{
   TestFrame()
   {
      super(); // call Frame's constructor
      setTitle("My First Java Window");
      setSize(300,200);
      setLocation(200,100);
      setBackground(java.awt.Color.lightGray);
      // add components here
      setVisible(true); // make window visible
   }
} // end TestFrame class
End example

Finally, you need a little test code to instantiate your window. You can do this by including the following method in this little class as well. All you have to do is instantiate an instance of your class, since the constructor method TestFrame does all the work to prepare and show it:

public static void main(String args[])
{
   TestFrame test = new TestFrame(); // instantiate ourself
}

Compiling this class with both these methods, and then invoking java TestFrame, produces the window shown in Figure 12.3, roughly in the center of your screen. If you code and run this, you will notice that you cannot close this window! You must kill it using Window's Task Manager because you have no code for "close," as yet.

Figure 12.3: A blank Java frame window.

An alternative to a Frame window is a Dialog window. This is used when you want to prompt the user for information and "block" while waiting for his or her response (called modal behavior). Typically, dialogs are used to display error messages or prompt for information needed to complete an action, such as prompting for a user ID and password. Coding a Dialog class is very similar to coding a Frame class, as shown in Listing 12.3.

Listing 12.3: An example of a Dialog class.
Start example
import java.awt.*;
import java.awt.event.*;
class TestDialog extends Dialog
{
   TestDialog(Frame parent)
   {
      super(parent, true); // true => modal
      setTitle("My First Java Dialog");
      setSize(200,100);
      setLocation(250,150);
      setBackground(java.awt.Color.lightGray);
      // add components here
   }
   public static void main(String args[])
   {
      TestFrame test = new TestFrame(); // parent frame
      TestDialog test2 = new TestDialog(test); // us
      test2.setVisible(true); // show the modal dialog
   }
} // end TestDialog class
End example

Notice that you must accept a parameter in the constructor now, namely a parent Frame object. This is because Dialog windows are always secondary to a Frame—the parent window. Thus, the Dialog class constructor requires a reference to the parent window, since the Dialog window will be modal to that. That is, the Frame window will be locked until the user closes the Dialog window. This parent reference is passed to the superclass constructor via the call to super(parent). (By the way, how often do you call your super parents?)

This superclass constructor call also specifies true. This indicates that Dialog should be modal to the parent. Because modality implies the call to show will block until the Dialog is closed, the call to setVisible is removed from the constructor, leaving that up to the caller to invoke when he or she is ready.

Notice that the size and location values are changed for this example so it would not overlap the previous example's window (shown in Figure 12.3). Notice also that the main method is changed to instantiate the new class, using the first Frame class as the parent. Finally, this gives the window arrangement shown in Figure 12.4.

Figure 12.4: A blank Java dialog window.

The Dialog class allows you to specify, via set methods, whether the dialog is modal and also resizable by the user. Dialogs are not minimizable or maximizable by the end user.

Note

Both Frame and Dialog extend Window, which extends Container, which in turn extends Component. So, in the end, many methods like setVisible are common to both. Furthermore, many methods like setBackground are shared with the basic components.

Adding Components to Containers: Layout Managers

Now the really fun part begins: populating your windows with components like push buttons and entry fields. In Java, you lay out your components by adding them to your container (a frame or dialog). That is, you first instantiate instances of component objects, as in the following:

Button okButton = new Button("OK");

Then, you add that object to your container, like this:

add(okButton);

But, what does this mean? Where does it go, since you do not specify anything equivalent to a row and column address? The answer is that it depends! It depends on what layout manager you specify for your container. Layout managers are classes that have predefined layout rules. In order to specify a particular layout manager, use the inherited container method setLayout:

setLayout(new xxxLayout());

Having done this with a desired xxxLayout class, your components are then added according to the rules of that layout manager class. This class ensures the components will not overlap each other. The layout manager options are shown in Table 12.7.

Table 12.7: Layout Managers in AWT.
Layout Manager | Description
BorderLayout | You can only add five components, and the window is divided into five regions: North (top), South (bottom), West (left), East (right), and Center (middle). When adding a component, you must specify which region to place it in. This is great for simple windows.
FlowLayout | Each component is inserted after the previous one, with some padding. If it fits horizontally, it will go there; otherwise, it starts on the next line. This is great for push buttons.
GridLayout | The window is divided into evenly sized cells. You must specify in the constructor how many rows and columns to allocate. Each add then takes the next sequential cell. Cell size is the largest display size of all the added components. This is great for "property sheet" windows.
GridBagLayout | The most complex and most flexible layout. The window is divided into cells, again, but each is sized to be as big as it needs to be for the component in it. This requires the use of the GridBagConstraints helper class to define the attributes of each populated cell. It is great for everything, if you can handle the coding.
CardLayout | This layout provides one component per "card," and only one card can be displayed at a time. Methods exist for cycling through the cards. This is great for wizards and notebooks, and any other rotating-page design.
Note

In addition to the layouts in Table 12.7, there is yet another option: specifying a layout manager of null, as in

setLayout(null);.

This layout mode indicates that you will hard-code the pixel x and y address of every component you add. This is not a recommended option, however, because it means you will produce a window that is resolution-dependent, one that does not gracefully allow for resizing by the user.

Let's have a closer look at the layout options in Table 12.7. Note that CardLayout is not covered here, though. We leave that to your research in the rare cases when you need it. Besides, Swing has a better option than CardLayout for designing paged notebooks (the JTabbedPane class). Aside from CardLayout, all the layout managers are still used in Swing, so your time and skills will not be wasted learning them.

Using BorderLayout

The example for the BorderLayout and all the subsequent layouts use only main Frame windows, not secondary Dialog windows. The code for frames is identical for dialogs, except you would extend Dialog instead of Frame, and require the parent main window as a parameter in the constructor. Most of your windows will be frames, while dialogs will be reserved mostly for error messages or other actions that require a user response before continuing.

Listing 12.4 gives the code for the example Frame window class extension, using the BorderLayout manager to show five components (an instruction line, an entry field prompt, an entry field, a List push button, and an OK push button).

Listing 12.4: An example of a Frame window class extension.
Start example
import java.awt.*;
import java.awt.event.*;
class TestBorder extends Frame
{
   // define GUI components
   private Label     instruction;
   private Label     prompt;
   private TextField entry;
   private Button    listButton;
   private Button    okButton;
   // Constructor
   TestBorder()
   {
      super();
      setTitle("My First Java Window");
      setSize(300,200);
      setLocation(200,100);
      setBackground(java.awt.Color.lightGray);
      // instantiate components
      instruction = new Label("Enter company number");
      prompt      = new Label("Number");
      entry       = new TextField(5);
      listButton  = new Button("List...");
      okButton    = new Button("Ok");
      // set window layout manager
      setLayout(new BorderLayout());
      // add components to window
      add(instruction,"North");
      add(prompt,     "West");
      add(entry,      "Center");
      add(listButton, "East");
      add(okButton,   "South");
      // show the window
      pack();
      setVisible(true);
   } // end constructor
   public static void main(String args[])
   {
      TestBorder test = new TestBorder();
   }
} // end TestBorder class
End example

Notice the call to the pack method at the end of the constructor, which is before the call to setVisible(true). The pack method will pack your fields and window automatically, removing extra space. This is handy; otherwise, you are dependent on setting your window size properly. Whether you use pack or not is up to you, but we will show all examples both with and without it, so you can see its effects.

Figure 12.5a shows the result of compiling and testing this class without the call to pack, while Figure 12.5b shows the result with the call to pack.

Figure 12.5a: BorderLayout without the call to pack.

Figure 12.5b: BorderLayout with the call to pack.

Notice that the call to pack does not take into account the width required by the title. That is a small price to pay for the much improved look of the window. Notice also that this example of a border layout turned out so well because there happened to be exactly five components, one for each of the five regions that BorderLayout defines. If you had more, what would you do? The answer involves nested containers, and will be discussed in the upcoming section on the Panel container.

Using FlowLayout

With a FlowLayout manager, you simply add each component, one after another, and they are appended to the screen. If they fit on the current row, they go there. If not, they wrap to the next line. If there simply is not enough space left to show the component, it is not shown. Listing 12.5 is a sample class that shows this layout manager.

Note

In the listings throughout the rest of this chapter, ** SAME ** is used to represent code that is the same as in previous listings.

Listing 12.5: A sample class using the FlowLayout manager.
Start example
class TestFlow extends Frame
{
   // define GUI components
   ** SAME **
   // Constructor
   TestFlow()
   {
      ** SAME **
      // instantiate components
      ** SAME **
      // set window layout manager
      setLayout(new FlowLayout());
      // add components to window
      add(instruction); add(prompt);
      add(entry); add(listButton);
      add(okButton);
      // show the window
      ** SAME **
   } // end constructor
   public static void main(String args[])
   {
      TestFlow test = new TestFlow();
   }
} // end TestFlow class
End example

Figure 12.6a shows the results without pack, and Figure 12.6b shows them with pack. Sizing the window moves the components around automatically to fit the new window size. That is, the components "flow" as required.

Figure 12.6a: FlowLayout without calling pack.
Click To expand
Figure 12.6b: FlowLayout with a call to pack.

Using GridLayout

The GridLayout manager divides the screen into a specified number of rows and columns. After that, the components are added to the next cell, moving left to right. Each cell will be as big as the biggest one necessary to hold the largest-sized component.

This is a tricky manager to use in our example case because it does not have an even number of components per row.

There is one component in the first row (the instruction label), three in the second row (the number label, the entry text field, and the list button), and one in the third row (the OK button). The solution is to specify three rows and three columns, then add dummy Label objects to the empty cells so they have something in them. Otherwise, all of the components would be shifted inappropriately. The resulting code is shown in Listing 12.6.

Listing 12.6: A sample class using the GridLayout manager.
Start example
class TestGrid extends Frame
{
   // define GUI components
   ** SAME **
   // Constructor
   TestGrid()
   {
      ** SAME **
      // instantiate components
      ** SAME **
      // set window layout manager. 3 rows, 3 cols
      setLayout(new GridLayout(3,3));
      // add components to window
      add(instruction);    // row 1, col 1
      add(new Label(" ")); // row 1, col 2
      add(new Label(" ")); // row 1, col 3
      add(prompt);         // row 2, col 1
      add(entry);          // row 2, col 2
      add(listButton);     // row 2, col 3
      add(new Label(" ")); // row 3, col 1
      add(okButton);       // row 3, col 2
      // show the window
      ** SAME **
   } // end constructor
   public static void main(String args[])
   {
      TestGrid test = new TestGrid();
   }
} // end TestGrid class
End example

Figure 12.7a shows the result without calling pack, while Figure 12.7b shows it with pack. Notice how the version without packing truncates the "Enter company number" text. This is because the hard-coded window size is insufficient to hold the widths required for each cell. The pack method nicely resizes everything to fit.

Figure 12.7a: GridLayout without calling pack.
Click To expand
Figure 12.7b: GridLayout with a call to pack.

You see here that the GridLayout manager is useful only in certain situations (well, more than you might think, as you will see in the section of this chapter that discusses Panel). Figure 12.8 is an example of a very useful fit for the grid layout manager, one that can be used to list "name, value" pairs of information.

Click To expand
Figure 12.8: Using GridLayout for a properties sheet.

Another good use for the GridLayout manager is for evenly sized push buttons, if that is a style you prefer over buttons that are sized to fit their text.

Figure 12.9 shows an example.

Figure 12.9: Using GridLayout for evenly sized buttons.

Using GridBagLayout

The GridBagLayout manager is far and away the most complex of all the layout managers to which you can code. However, when all the previously mentioned layout managers fail to meet your purposes, you can be sure that this one will work—always! Like GridLayout, it divides the screen into cells.

However, you do not specify the rows and columns for the cells when you instantiate the layout object. Instead, this is done implicitly as you add each component.

For each component you add, you specify the row and column coordinates (0,0-based), and the number of rows and columns this component is to span. You can also specify whether the component is allowed to grow and shrink with the sizing of the window (horizontally and/or vertically), whether it is to be stretched to fit the cell(s) it is in, and whether and how it is to be aligned in the cell(s).

There is potentially a lot of extra information to be specified with each component as it is added, which is why there is relative complexity. How is all this extra information specified? Not via parameters to the add method, as you might expect. That would make this method too complicated and would require numerous parameters. Rather, in true object-oriented fashion, you instantiate another class, GridBagCon-straints, to contain all this information. With this approach, you can specify all your defaults once. Then, you change only the defaults you need to for each component that you add. The non-object-oriented part of this class is that all this information is specified by explicitly changing variables in the object, versus the better approach of using set methods.

Here, then, are the steps needed to use a GridBagLayout for a layout manager:

  1. Instantiate an instance of GridBagLayout with no parameters.

  2. Instantiate an instance of GridBagConstraints with no parameters.

  3. Specify the GridBagLayout object as the layout manager, via setLayout, as usual.

  4. Set all your preferred default values in the GridBagConstraints class.

  5. For each component to be added, do the following:

    1. Set the required values of the GridBagConstraints object uniquely for this component (such as the row and column address—or gridy, gridx variables).

    2. Call the GridBagLayout method setConstraints, passing your component and the GridBagConstraints object.

    3. Call the add method of the window to add the component, as usual.

Table 12.8 shows the important variables that need to be set in GridBagConstraints.

Table 12.8: GridBagConstraints Variables.

Variable

Description

gridy, gridx

The row and column position (zero-based) for this component, where 0,0 is the upper left corner.

gridheight, gridwidth

The numbers of rows and the number of columns this component will occupy. The default is one each.

weighty, weightx

Does this component grow in either direction when the window is sized? If yes, specify 1.0, otherwise specify 0. The default is 0. Typically, specify 1.0 for weightx for TextFields, and 1.0 for weightx and weighty for Lists and TextAreas. At least one component should have a non-zero value.

fill, anchor

What do you want to do when the cell(s) are larger than the component?

  • Stretch the component: set fill to HORIZONTAL, VERTICAL, or BOTH. The default is NONE, meaning do not stretch in either direction.

  • Align the component: set anchor to CENTER, NORTH, SOUTH, WEST, EAST, NORTHEAST/WEST, or SOUTHEAST/WEST. The default is CENTER.

The tricky part (although one that will eventually come very easily to you) is determining the gridy/x and gridheight/width to specify for each component. Here is a little algorithm you can follow to get you going:

  1. Lay out the intended screen in your mind or on paper.

  2. Determine how many unique rows there are (three, in our running example). If you have a list box with three push buttons beside it, vertically aligned, then you would count three rows for that. Draw a line horizontally across the bottom of each component.

  3. Determine how many unique columns there are (three, in our running example). Draw a line vertically at the start of each component.

  4. Determine the following for each component:

    1. The row and column in which it should start.

    2. If there are no components beside it, its width is the number of remaining columns. Our first instruction line, for example, will span all three columns. If there are components beside it, the width is the difference between this component's starting column and the one beside it.

    3. If there are no components beside it, its height is one. If there are components beside it, its height is the number of components, vertically, beside it. For example, the list box with three push buttons vertically beside it will have a height of three (or more, if you want padding).

Looking at our running example, Figure 12.10 shows how it would be divided up.

Figure 12.10: Planning for GridBagLayout.

All right, then, let's see the code! The result is shown in Listing 12.7.

Listing 12.7: A sample class using the GridBagLayout manager.
Start example
class TestGridBag extends Frame
{
   // define GUI components
   ** SAME **
   // Constructor
   TestGridBag()
   {
      ** SAME **
      // instantiate components
      ** SAME **
      // set window layout manager
      GridBagLayout      gbl = new GridBagLayout();
      GridBagConstraints gbc = new GridBagConstraints();
      setLayout(gbl);
      gbc.fill  = GridBagConstraints.HORIZONTAL; // stretch hor'ly
      gbc.anchor= GridBagConstraints.SOUTH; // bottom justify
      // add components to window
      gbc.gridy=0; gbc.gridx=0; gbc.gridheight=1; gbc.gridwidth=3;
      gbl.setConstraints(instruction,gbc);
      add(instruction);    // row 1, cols 1—3
      gbc.gridy=1; gbc.gridx=0; gbc.gridheight=1; gbc.gridwidth=1;
      gbl.setConstraints(prompt,gbc);
      add(prompt);         // row 2, col 1
      gbc.gridy=1; gbc.gridx=1; gbc.gridheight=1; gbc.gridwidth=1;
      gbl.setConstraints(entry,gbc);
      add(entry);          // row 2, col 2
      gbc.gridy=1; gbc.gridx=2; gbc.gridheight=1; gbc.gridwidth=1;
      gbl.setConstraints(listButton,gbc);
      add(listButton);     // row 2, col 3
      gbc.gridy=2; gbc.gridx=0; gbc.gridheight=1; gbc.gridwidth=3;
      gbl.setConstraints(okButton,gbc);
      add(okButton);       // row 3, cols 1—3
      // show the window
      pack(); // try it with and without
      setVisible(true);
   } // end constructor
   public static void main(String args[])
   {
      TestGridBag test = new TestGridBag();
   }
} // end TestGridBag class
End example

And what does this produce? Figures 12.11a and 12.11b show the windows, with and without pack.

Figure 12.11a: GrigBagLayout without a call to pack.

Figure 12.11b: GrigBagLayout with a call to pack.

Why, without packing, do the components clump into the center? Because we did not specify a weighty or weightx nonzero value for any component, so the extra space is simply given to the edges. Otherwise, it would be distributed among the weighted components.

Figure 12.12 shows what the window looks like if the OK push button is given a weighty, weightx of 1.0 each, and a fill value of GridBagConstraints.BOTH is specified. (See the TestGridBag2.java file on the CD-ROM for the source code of this window.)

Figure 12.12: Using positive weights in GridBagConstraints.

Remember, users can resize your windows, even if you pack them so that they look good initially. Therefore, it is important to identify those components you would like to grow when the window is sized, by specifying nonzero values for weightx and weighty.

Specifying Padding between Components

When you start to fine-tune your user interfaces, you might be interested in changing the Java-supplied default padding between the components that you add. This can be done with each of the layout managers by specifying horizontal and vertical "gap" values on the constructors. Alternatively, you can use the setHgap and setVgap methods supplied by each layout manager class, except for the GridBagLayout class, which requires you to specify the ipadx and ipady variables in the GridBagConstraints object.

The values you specify are in pixels, and you might find that five is a good number if you do not like a tight look. In the BorderLayout example, changing the setLayout line to look like the following results in Figure 12.13:

Figure 12.13: Using padding between components.
setLayout(new BorderLayout(5,5));

Notice the padding between the components. This is a five-pixel padding.

Nesting Layout Managers: Panels

So far, you have seen how you can add components to your Frame or Dialog window object directly, using one of the layout manager classes to define the placement of the added components.

In Java, you can also define subparts of a window, accumulate them to produce a single whole window, and reuse them in many different windows. This is done using the Panel class. Like Frame and Dialog, Panel allows components to be added to it, and requires a layout manager class to define how the components will be arranged. However, Panel has no visible borders or other frame pieces because it is meant to be used as part of a window.

Using panels, you can greatly increase your flexibility in choosing a layout manager because you can add panels to a Frame window instead of adding components directly. This way, one area of your screen can use a panel with one layout manager that is appropriate for it, and another can use another panel with another layout manager that is appropriate for it. Think about the BorderLayout layout manager—it can only allow five components to be added, one each in the North, South, West, East, and Center regions. This is very restrictive, except that each of those components could be a panel that contains numerous nested components.

Consider the OK push button at the bottom of our running example. Imagine you wanted to have a Cancel push button there, as well. With the BorderLayout manager, which was one of the best options so far, you would be in trouble, since the South region already has the OK button. How could you add the new Cancel button? The answer is to put both the OK and the Cancel buttons into a separate Panel object, and add that to the South region. Because Panel inherits from the Component class, just as Button and the other basic components do, you can add a Panel object wherever you can add a Component object.

Let's try this in an example. First, we will create a new class that extends Panel, much as Frame was extended. Then, we will define the push buttons in this class and add them to the panel, after specifying the layout manager. Listing 12.8 provides the code.

Listing 12.8: Using Panel instead of Frame or Dialog.
Start example
import java.awt.*;
import java.awt.event.*;
class TestPanel extends Panel
{
   // define GUI components
   private Button okButton;
   private Button cancelButton;
   // Constructor
   TestPanel()
   {
      super();
      // instantiate components
      okButton    = new Button("Ok");
       cancelButton= new Button("Cancel");
      // set window layout manager
      setLayout(new FlowLayout());
      // add components to window
      add(okButton);
      add(cancelButton);
   } // end constructor
} // end TestPanel class
End example

Now, we will modify the earlier sample program that used a BorderLayout layout manager. Instead of defining, instantiating, and adding an OK button to the South region, we define, instantiate, and add a "buttons" TestPanel object (the class that was just defined) to the South region. This is shown in Listing 12.9.

Listing 12.9: Placing a Panel object inside a Frame window.
Start example
import java.awt.*;
import java.awt.event.*;
class TestBorderWithPanel extends Frame
{
   // define GUI components
   private Label     instruction;
   private Label     prompt;
   private TextField entry;
   private Button    listButton;
   private TestPanel buttons; // OK and Cancel button panel
   // Constructor
   TestBorderWithPanel()
   {
      super();
      setTitle("My First Java Window");
      setSize(300,200);
      setLocation(200,100);
      setBackground(java.awt.Color.lightGray);
      // instantiate components
      instruction = new Label("Enter company number");
      prompt      = new Label("Number");
      entry       = new TextField(5);
      listButton  = new Button("List...");
      buttons     = new TestPanel();
      // set window layout manager
      setLayout(new BorderLayout(5,5));
      // add components to window
      add(instruction,"North");
      add(prompt,     "West");
      add(entry,      "Center");
      add(listButton, "East");
      add(buttons,    "South");
      // show the window pack();
      setVisible(true);
   } // end constructor
   public static void main(String args[])
   {
      TestBorderWithPanel test = new TestBorderWithPanel();
   }
} // end TestBorderWithPanel class
End example

This results in a completely reusable class that defines common buttons (much like the common function-keys record formats), shown in Figure 12.14. It offers not only increased flexibility in choosing a Frame layout manager, but overall code reduction and that all-important goal of encapsulation.

Figure 12.14. A reusable panel of buttons.

Your company's design standards can be encapsulated by common Panel classes, and easily changed if desired. You might choose to have all buttons the same size, in which case you would simply change the TestPanel class to use a GridLayout. Then, all users of it would get the benefit, as shown in Figure 12.15.

Figure 12.15. A reusable panel of buttons with evenly sized buttons.

Panels offer tremendous flexibility and options. You should exploit that power. Panels can even be nested inside other panels to maximize your options. Suppose this entire little "customer number" prompt became a commonly required element in many other windows, perhaps even more complex than this one. You could make this entire class, which currently extends Frame, a completely reusable panel by changing it to extend Panel instead. In such a case, you would remove the size and location settings, since they would probably be specified by the parent window. They are still valid if you choose to leave them, however.

Another advantage of panels is that they allow you to give subsections of your window unique attributes, such as colors and fonts. Listing 12.10 shows what a new version of the example class might look like. (Note that it is almost identical to the previous example, only slightly smaller due to the loss of the Frame-tailoring code.)

Listing 12.10: Converting the entire client area to a panel.
Start example
import java.awt.*;
import java.awt.event.*;
class CompanyPrompt extends Panel
{
   // define GUI components
   private Label     instruction;
   private Label     prompt;
   private TextField entry;
   private Button    listButton;
   private TestPanel buttons;
   // Constructor
   CompanyPrompt()
   {
      super();
      // instantiate components
      instruction = new Label("Enter company number");
      prompt      = new Label("Number");
      entry       = new TextField(5);
      listButton  = new Button("List...");
      buttons     = new TestPanel();
      // set window layout manager
      setLayout(new BorderLayout(5,5));
      // add components to window
      add(instruction,"North");
      add(prompt,     "West");
      add(entry,      "Center");
      add(listButton, "East");
      add(buttons,    "South");
   } // end constructor
} // end CompanyPrompt class
End example

Now, the main window class code becomes very simple, since this subpanel is the only part added to it. Simply use a BorderLayout and add an instance of this new Company-Prompt panel to it, in the Center region, as shown in Listing 12.11.

Listing 12.11: Placing a client-area panel inside a frame.
Start example
import java.awt.*;
import java.awt.event.*;
class TestNestedPanels extends Frame
{
   // define GUI components
   private CompanyPrompt companyprompt;
   // Constructor
   TestNestedPanels()
   {
      super();
      setTitle("My First Java Window");
      setSize(300,200);
      setLocation(200,100);
      setBackground(java.awt.Color.lightGray);
      // instantiate components
      companyprompt = new CompanyPrompt();
      // set window layout manager
      setLayout(new BorderLayout(5,5));
      // add components to window
      add(companyprompt, "Center");
      // show the window
      pack();
      setVisible(true);
   } // end constructor
   public static void main(String args[])
   {
      TestNestedPanels test = new TestNestedPanels();
   }
} // end TestNestedPanels class
End example

This gives the now-familiar results shown in Figure 12.16.

Figure 12.16: A reusable prompt panel.

While it might appear that you are back where you started, you are actually much further ahead. You now have a completely reusable, self-contained "prompt" that can be embedded anywhere you need it. Actually, it is not totally self-contained yet, since it does not process user input in any way. You will see how to deal with that later in this chapter.

Advanced Components: Menu Bars, Pull-Downs, and Popups

In COBOL applications, you typically start with a menu that allows the user to choose an application area to go into. For example, a typical menu might allow the user to work with customers, enter data into customer accounts, and keep track of inventory. Once a menu option is chosen by the user, the appropriate COBOL program is launched.

Menu Bars and Pull-Down Menus

You will be hard-pressed to find a 3270/5250-style traditional menu in a GUI application. Instead, these user interfaces have menu bars, such as the one shown in Figure 12.17.

Click To expand
Figure 12.17: Menu bars, menus, and menu items.

This example is composed of the following:

  • A menu bar object of class MenuBar.

  • Two menu objects of class Menu, for the File and Edit menus.

  • Another menu object for the "Work with" nested cascading menu.

  • A number of menu item objects of class MenuItem for items like Print, Exit, and Customers.

The steps to take to define these menus inside a Java Frame class object are as follows:

  1. Create the MenuBar object.

  2. Create the Menu objects for the pull-down menus and nested menus.

  3. Add the Menu objects to the MenuBar object.

  4. Create the MenuItem objects.

  5. Add the MenuItem objects and nested Menu objects to the Menu objects.

  6. Add the MenuBar object to the Frame window, using setMenuBar.

Listing 12.12 shows the code to produce the example in Figure 12.17. (Before too long, you will learn the important concept of how to be notified when a menu item is selected.)

Listing 12.12: Defining a menu that contains nested menus, which contain menu items.
Start example
class TestMenu extends Frame
{
   private MenuBar menubar;
   private Menu fileMenu, editMenu, appMenu;
   private MenuItem printMI, exitMI;
   private MenuItem customersMI, accountsMI, inventoryMI;
   // Constructor
   TestMenu()
   {
      super();
      setTitle("Work Central");
      setSize(300,200);
      setLocation(200,100);
      // instantiate menu bar and menus
      menubar = new MenuBar();
      fileMenu = new Menu("File");
      editMenu = new Menu("Edit");
      appMenu = new Menu("Work with");
      menubar.add(fileMenu);
      menubar.add(editMenu);
      // instantiate menu items
      printMI = new MenuItem("Print");
      exitMI = new MenuItem("Exit");
      // instantiate "Edit" menu items—not shown
      customersMI = new MenuItem("Customers");
      accountsMI = new MenuItem("Accounts");
      inventoryMI = new MenuItem("Inventory");
      // populate menus with menu items
      fileMenu.add(printMI);
      fileMenu.add(exitMI);
      fileMenu.addSeparator();
      fileMenu.add(appMenu);
      // populate "Edit" menu—not shown
      appMenu.add(customersMI);
      appMenu.add(accountsMI);
      appMenu.add(inventoryMI);
      // add menubar to window
      setMenuBar(menubar);
      setVisible(true);
   } // end constructor
   public static void main(String args[])
   {
      TestMenu test = new TestMenu();
   }
} // end TestMenu class
End example

You can also include "toggle-able" choices in your menu items, which the user can turn on or off. If the item is turned on, a check mark is displayed beside it. An example is shown in Figure 12.18.

Figure 12.18: A menu item with a toggle.

These items are created using CheckboxMenuItem instead of MenuItem. You can set the initial checked state to "on" in the constructor. Listing 12.13 shows the code added to the example to achieve this effect.

Listing 12.13: Defining "toggle-able" menu items.
Start example
private Menu      viewMenu;
   private CheckboxMenuItem editModeMI, fontModeMI;
      viewMenu  = new Menu("View");
      menubar.add(viewMenu);
      editModeMI  = new CheckboxMenuItem("Read only");
      fontModeMI  = new CheckboxMenuItem("Large font", true);
      viewMenu.add(editModeMI);
      viewMenu.add(fontModeMI);
End example

Notice that you can also set a CheckboxMenuItem's state by calling its setState method and specifying true ("show check mark") or false. You can query its state by calling its getState method, which returns true or false.

Popup Menus

In the world of GUI applications, a common part of the user interface is the type of menu that pops up when you click the mouse. For example, you might have a list box that shows a popup menu with the options "Insert," "Remove," and "Change" when the user clicks on that box.

These menus, which are often referred to as context menus, have become popular because they offer a way to support additional functionality without taking up screen real estate. For example, you do not have to find room in your window for "Insert," "Remove," and "Change" push buttons. These popups, when used with lists, replace the display-file practice of including an input-capable entry field in front of each item in the list where users can type arcane one- or two-digit options.

Java supports popup menus beginning with version 1.1. They are coded using the PopupMenu class, which extends the Menu class discussed previously. To code a popup menu, do the following:

  1. Declare a variable of class type PopupMenu, and instantiate it, optionally specifying a name for the popup, like this:

    PopupMenu popup = new PopupMenu("Work with");

  2. Declare and instantiate instances of MenuItem to contain the popup values, as in the following:

    MenuItem insertMI = new MenuItem("Insert...");
MenuItem removeMI = new MenuItem("Remove");
MenuItem changeMI = new MenuItem("Change...");
    Note

    The ellipses ("…") are a convention for menu items that display another dialog, which presumably the "Insert" and "Change" options would have to do.

  3. Add the popup menu to the frame, dialog, or panel:

    add(popup);

  4. Enable the part you want to support popups to get the mouseReleased event (you will learn more about events soon):

    myListBox.addMouseListener(this);

    You also have to change your class to add implements MouseListener.

  5. Define empty methods required by the MouseListener interface but not of interest here:

    public void mouseClicked(MouseEvent event) {}
publblic void mousePressed(MouseEvent event) {}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}

  6. Define the method mouseReleased, which is of interest. It will be called by Java when the right mouse button is pressed and released while the cursor is in the target part—in this case, in part myListBox:

    public void mouseReleased(MouseEvent event)
{
   Component part = event.getComponent();
      if (event.isPopupTrigger() && (part == myListBox))
         popup.show(part, event.getX(), event.getY());
}

Note the use of isPopupTrigger, which returns true if the right mouse button is used in Windows (or the left mouse button on other systems that use button for popups, like UNIX). Also, note the use of getX and getY, which return the x and y pixel address of the cursor, required by the popup.show method. A full example is shown in Listing 12.14.

Listing 12.14: Defining a popup menu, and displaying a popup menu.
Start example
import java.awt.*;
import java.awt.event.*;

class TestPopup extends Frame implements MouseListener
{
   private List      myListBox;
   private PopupMenu popup;
   private MenuItem  insertMI,changeMI,removeMI;
   private TestPanel buttons;

   TestPopup()
   {
      super("My Todo List"); // sets title
      setLocation(200,100);
      myListBox   = new List(3);
      myListBox.add("item 1");
      myListBox.add("item 2");
      buttons     = new TestPanel();
      popup       = new PopupMenu("Work with...");
      insertMI = new MenuItem("Insert...");
      removeMI = new MenuItem("Remove");
      changeMI = new MenuItem("Change...");
      popup.add(insertMI); popup.add(removeMI);
      popup.add(changeMI); add(popup);
      myListBox.addMouseListener(this);
      setLayout(new BorderLayout(5,5));
      add(new Label("Items To Do"),"North");
      add(myListBox,  "Center");
      add(buttons,    "South");
      pack();
      setVisible(true);
   } // end constructor

   public void mouseClicked(MouseEvent event) {}
   public void mousePressed(MouseEvent event) {}
   public void mouseEntered(MouseEvent event) {}
   public void mouseExited(MouseEvent event) {}
   public void mouseReleased(MouseEvent event)
   {
      Component part = event.getComponent();
      if (event.isPopupTrigger() && (part == myListBox))
         popup.show(part, event.getX(), event.getY());
   }
   public static void main(String args[])
   {
      TestPopup test = new TestPopup();
   }
} // end TestPopup class
End example

This results in the popup shown in Figure 12.19, which appears when a user right-clicks in the list box. Getting notified when a menu item is selected from a popup menu is identical to getting notified when a menu item is selected from a pull-down menu. You will learn about this detail soon.

Figure 12.19: A popup menu.

Menu-Item Shortcuts

You will frequently want to supply keyboard shortcuts for menu bar items and popup menu items. These are key combinations, such as Ctrl+R or Ctrl+Shift+A, that act as alternatives to selecting item from the menu. Shortcuts can be specified when creating your menu items, as the second parameter on the new MenuItem or CheckboxMenuItem calls. They are specified by creating an instance of the MenuShortcut class. You simply specify a character to use as the shortcut.

In Windows, Java automatically assigns the shortcut to be Ctrl plus the specified character, since Ctrl is the standard for shortcut keys in Windows. You can, optionally, specify that the Shift key is required by specifying true for the second parameter. For example, the following code changes the menu example to supply shortcuts of Ctrl+P for Print, and Ctrl+Shift+E for Exit:

MenuShortcut printSC = new MenuShortcut('p');
MenuShortcut exitSC  = new MenuShortcut('e',true);
printMI = new MenuItem("Print", printSC);
exitMI  = new MenuItem("Exit", exitSC);

This allows the user to press these key combinations instead of selecting the menu items. (It looks the same to your program.) It also visually changes the menu items to reflect these shortcuts, as shown in Figure 12.20. (See the TestShortCut.java file on the supplied CD-ROM for the source.)

Figure 12.20: Menu shortcuts.

Unfortunately, there is no way to assign a function key as a shortcut. This is not that uncommon, especially for migrated OS/390 and OS/400 user interfaces. For these, you will have to insert the unique code yourself to trap the keyboard input and check for the function keys that you want to enable.

Processing User Input—Events

Congratulations on having created some great-looking user interfaces. Now, how do you process input from them? In COBOL, your programs wait after a read or accept, and then process the user input from the input fields. Reading the screen is a synchronous, or modal, operation. Your processing code occurs sequentially after your screen-input operation.

In Java, your input-processing code will not be sequential after your setVisible method call because the setVisible method, except for modal dialogs, returns control to you immediately. This happens before the user has entered any input. It is an asynchronous, or modeless, operation. For modal dialogs, it returns only after the dialog has been dismissed via the dispose method call.

In COBOL, you really have only two forms of input information: the text entered by the user, and the key pressed by the user (such as Enter or a function key). In essence, then, the pressing of a key is the only user event with which you are concerned. You process the screen values after that event. In Java and in all graphical user interfaces, by contrast, you have many more events to be concerned with, including these:

  • Pressing a push button.

  • Selecting an item in a list.

  • Selecting a menu item.

  • Checking a check box or radio button.

  • Typing text.

  • Clicking a mouse button.

You do not have to be concerned with all the possible events that a user can trigger, but you will frequently want to monitor a few of them to provide immediate error-checking and visual feedback. For example, you might want to prevent users from typing alphabetic characters in a numeric entry field. (This is your job, unfortunately, since Java does not supply any built-in entry-field validity-checking, the way 3270 and 5250 do.) You might also want to "gray out" or disable certain components when a radio button is selected, and then re-enable them when the button is deselected, and so on.

Note

Most of your windows will have an OK push button that will be the closest functional thing to the Enter key event in 3270 or 5250 processing. Most of your function keys will also end up becoming push buttons in a Java GUI screen. For example, a Cancel push button, a Refresh push button, a List or Browse push button, and so on.

If the call to setVisible is roughly equivalent to writing and then reading a screen in COBOL, but it returns control immediately, how do you get control when an event happens? In Java, these events are Java objects (of course!) that are sent to your own class if you tell Java to do that.

The Java event model described in the following sections for AWT is identical in Swing, so your time is well-spent learning it. Furthermore, as you learn in the final chapter, this event model also applies to Java Beans.

What Are These Java Objects?

These event objects are instances of various classes (depending on the event that happened) that inherit from, or extend, the Java class java.awt.AWTEvent—the root event class. Each unique event, such as ActionEvent for push button presses, sends your program an object of the appropriate event class. From this object, you can query (via method calls on the object) information such as the GUI component that triggered the event (using the event.getSource method call).

How do you tell Java to send these event objects to your class? Well, let's think about this problem. You need an architecture that allows two things:

  1. You to tell Java which events you are interested in.

  2. Java to call your code when one of the events you are interested in happens.

Item 1 is easy. Java just supplies some APIs in every component class that you can call to register that you are interested in particular events. Actually, there are so many events, you register which category of events you are interested in (such as mouse, keyboard, window, text, selection, or action for "press"-type events). You decide when Java calls if the particular event (for example, a mouse move versus a mouse press) is interesting, and you ignore those that are not.

Item 2 is more interesting, though. When a registered event happens, Java wants to call a particular method in your class. In essence, when you register in step 1, you have to give Java a method of yours that it can "call back" when the event happens. How can you do call-backs in Java? Java doesn't have function pointers, remember. It uses interfaces, covered in chapter 9, to allow for any code to be callable from Java.

Java supplies interfaces for each type of event, and when you register your interest in that type of event, you have to give Java an object that implements that interface. This means your object will supply implementations of the method signatures in that interface. Java holds onto this object, and when a particular event happens, it calls the appropriate interface-defined method in your object. Your code for that method can then do whatever it wants in response to that event.

Let's look at this in detail. To process events, you have to do three things:

  1. Indicate that your class is capable of responding to these events by including implements xxxListener on your class definition, where xxx is different for each category of event in which you are interested. For example, implements ActionListener causes the system to send you action events, such as what happens when a push button is pressed (versus, say, text events when text is typed).

  2. Supply a method in your class that will be called by the Java runtime for specific events. These methods have to use the exact names and parameter types (that is, the signatures) that Java defines for each type. These are all defined in the xxxListener interfaces, which is defined in the java.awt.event package. For example, for action events, you are required to supply the method public void actionPerformed(ActionEvent event). In these methods, you process the event and query inputs and states as required.

  3. After creating each GUI component, such as a push button, you must register that it is to send its events to your class. You do this using the addxxxListener (your-class-object); method that all input-capable Java components support. For example, if you implement the ActionListener interface in your actual window class, you might code the following for a push button:

    okButton.addActionListener(this);

Before going into more details about all the event types and their associated listeners, let's consider an example. We will use the version of the running example where all of the components have been moved to a separate CompanyPrompt class that extends Panel, changing the window class that added this prompt to the Center region of a border layout.

The first change will be to add a message line at the bottom of the window to display output messages for debugging and learning purposes. Generally, this is a worthwhile addition to all your windows because it is a great way to display messages to the user. It is just like the message subfile line that OS/400 programmers are used to in display files.

By implementing the message line as a read-only TextArea component, a horizontal scroll bar will be displayed for large amounts of text. Another option is to use a Choice component so that multiple messages could be displayed at once, but we want to keep this example simple. Listing 12.15 is the revised class with this new message line added.

Listing 12.15: Adding a message line to the window.
Start example
import java.awt.*;
import java.awt.event.*;
class TestEvents extends Frame
{
   private CompanyPromptWithEvents companyprompt;
   private TextArea      msgLine;
   TestEvents()
   {
      super("TestEvents");
      setSize(300,200);
      setLocation(200,100);
      setBackground(Color.lightGray);
      // instantiate components
      msgLine       = new TextArea("msg line",1,1,
         java.awt.TextArea.SCROLLBARS_HORIZONTAL_ONLY);
      msgLine.setEditable(false); // read-only
      msgLine.setForeground(java.awt.Color.red);
      companyprompt = new CompanyPromptWithEvents(msgLine);
      // set window layout manager
      setLayout(new BorderLayout(5,5));
      // add components to window
      add(companyprompt, "Center");
      add(msgLine,       "South");
      // show the window pack();
      setVisible(true);
   } // end constructor
   public static void main(String args[])
   {
      TestEvents test = new TestEvents();
   }
} // end TestEvents class
End example

This code changes the line that instantiates a new CompanyPrompt to pass in msgLine as a parameter to the CompanyPrompt constructor. The CompanyPrompt class is changed to accept a TextArea component reference as a parameter to the constructor so that this class can write to that TextArea when processing events. Listing 12.16 is the revised CompanyPrompt class, renamed to CompanyPromptWithEvents, with code added to process the List push button being pressed.

Listing 12.16: Adding code to process the pressing of the List button.
Start example
import java.awt.*;
import java.awt.event.*;
class CompanyPromptWithEvents extends Panel
                           implements ActionListener
{
   private TextArea  msgLine;
   // define GUI components
   private Label     instruction;
   private Label     prompt;
   private TextField entry;
   private Button    listButton;
   private TestPanel buttons;
   // Constructor
   CompanyPromptWithEvents(TextArea msgLine)
   {
      super();
      this.msgLine = msgLine;
      // instantiate components
      instruction = new Label("Enter company number");
      prompt      = new Label("Number");
      entry       = new TextField(5);
      listButton  = new Button("List...");
      listButton.addActionListener(this);
      buttons     = new TestPanel();
      // set window layout manager
      setLayout(new BorderLayout(5,5));
      // add components to window
      add(instruction,"North");
      add(prompt,     "West");
      add(entry,      "Center");
      add(listButton, "East");
      add(buttons,    "South");
   } // end constructor
   // Process button actions
   public void actionPerformed(ActionEvent event)
   {
      if (event.getSource() == listButton)
      msgLine.setText("List button pressed!");
   }
} // end CompanyPromptWithEvents class
End example

The result of running this and pressing the List push button is the window shown in Figure 12.21.

Figure 12.21: Processing ActionEvent for a push button.

You see how easy it is to get control when user-initiated events occur. Now that you have seen the Java infrastructure for event processing, let's solve the thorny little problem that all the examples so far have shared: you cannot close the windows except by killing them.

The event involved here is WindowEvent, which is sent for any interesting window-related user actions, such as closing, maximizing, minimizing, and restoring. Closing the window is the one activity that Java does not handle on its own, so you are forced to handle it yourself. Because handling this event is likely to be commonly required, it makes sense to create a new class that processes only this event, by implementing the WindowListener interface. You can then create an instance of it to use in any window class you have. The code for this class is given in Listing 12.17.

Listing 12.17: A reusable class for processing the window-closing event.
Start example
import java.awt.*;
import java.awt.event.*;
public class FrameListener implements WindowListener
{
   private Window owner;       // window we are monitoring
   private boolean mainWindow; // is this a main window?
   // constructor
   public FrameListener(Window owner, boolean mainWindow)
   {
      this.owner = owner;
      this.mainWindow = mainWindow;
   }
   // called when user closes window...
   public void windowClosing(WindowEvent e)
   {
      owner.dispose();
      if (mainWindow)
      System.exit(0);
      return;
   }
   public void windowOpened(WindowEvent e) {}
   public void windowClosed(WindowEvent e) {}
   public void windowIconified(WindowEvent e) {}
   public void windowDeiconified(WindowEvent e) {}
   public void windowActivated(WindowEvent e) {}
   public void windowDeactivated(WindowEvent e) {}
} // end FrameListener class
End example

Notice that the constructor in this class takes in two parameters, a Window object and a Boolean variable. The former is simply the window being monitored; you will need this to close that window. The latter tells you whether this is the main window in the application. You need to ask for this because, when closing the main window, you have to do something special—call System.exit to exit the entire application. (This is like STOP RUN in COBOL.)

Nobody else will do this for you. If you do not do this, the window will disappear, but the JVM will keep running in the background. Look now at the windowClosing method. This is what Java will call when the user closes the window by clicking the x in the upper right corner of a window or selecting Close from the system menu. All it has to do is call the dispose method on the window being monitored, and that window will be destroyed. It also calls System.exit, as discussed, for the main window in the application, since that usually means "kill the application."

What are all those other empty methods? If you look at the WindowListener interface in the java.awt.event package, you will see that it defines all these method signatures. Recall that to implement an interface in your own class, you must implement every one of the methods defined in the interface, but since you do not need to monitor for any of these other window events, you can just leave the methods empty.

For those xxxListener interfaces that have multiple methods in them, Java supplies some helper classes called xxxAdaptor. They implement the xxxListener class and supply empty versions of the methods, as we have done here. The advantage is that you can choose to extend these classes and only code the methods you want to override, instead of having to define all the empty methods, as in Listing 12.17. Because you can only extend one class in Java, you cannot use these if your class is already extending another class, or if it someday might. You are better off, in our opinion, implementing the interface and typing in the empty methods.

Now, finally, let's see how to put this common new class to use. You would only need to change the last TestEvents class to use this class, by changing its constructor to do the following:

  • Instantiate an object of the class:

    FrameListener closer = new FrameListener(this, true);

    Notice that the parameters match what the class's constructor expects.

  • Register to the system that you want this new object to handle Window events:

    addWindowListener(closer);

You could code this anywhere prior to the setVisible(true) line of code.

Because this class extends Frame, calling addWindowListener actually calls this method in the Frame class. It is a Frame, because it inherits from one. You can now finally close your windows. That's always a great feature to support in production-level applications!

All of the typically used xxxListener interfaces are shown in Table 12.9. See the JDK documentation regarding these java.awt.event interfaces for details about the methods they require you to implement if you want to monitor for these events. Each of these methods will be passed an appropriate xxxEvent object as its first parameter, so you should also see the JDK documentation for these java.awt.event classes to find out about the information you can retrieve from these event objects.

Table 12.9: Event-Listener Interfaces.
Listener | Description
ActionListener | An action occurs, such as the press of a button, a mouse double-click on a list entry, or the selection of a menu item or popup item.
WindowListener | A window event occurs, such as a window closing.
FocusListener | A component gains or loses focus.
ItemListener | Something is selected, such as a check box, radio button, or item in a list, or a CheckboxMenuItem is toggled.
MouseListener | The mouse has been clicked, or it enters/exits a component's visual area.
MouseMotionListener | A mouse is being dragged or moved over a component.
KeyListener | A key on the keyboard has been typed. Use this to capture function keys, for example, or to verify characters as they are typed.
TextListener | The text in a TextField or TextArea has been changed.

Most of your event methods will need to determine exactly which component triggered the event. For example, your screen will often have multiple push buttons and multiple menu-bar items, but only one ActionPerformed method to process all of them. All event objects support the getSource method that returns an Object reference to the specific component or menu item responsible for triggering the event. By comparing this value to your component reference variables, you can determine what to do:

Object component = event.getSource();
if (component == okButton)
   ...
else if (component == openMenuItem)
   …

Processing Input

To write the code that implements the appropriate listener interface, you have to add code in your event-processing methods to retrieve information from the screen. This is done using appropriate get methods on your components. As part of your processing of input, you might also find yourself needing to change the state of something, for which there is usually an equivalent set method defined in the component class.

Table 12.10 provides a summary of some of the more frequently used methods in each component for querying and setting values. (Remember that all item and text position numbers are zero-based in Java.)

Table 12.10: Common Set and Get Methods for AWT Components.
Component | Query Methods | Set Methods
Button | getLabel to return the button text. | setLabel to set the button text.
Checkbox—Group | getSelectedCheckbox to return the selected radio button. | setSelectedCheckbox to set the selected radio button.
Checkbox | getLabel to return the button text. getState to return the selected state getCheckboxGroup for radio buttons. | setLabel to set the button text. setState to change the selected state.
MenuItem Checkbox | getLabel to return the item text. setState to set the selected state. | setLabel to set the item text. getState to return the selected state.
Choice | getSelectedIndex to return the integer index of the selected choice. getSelectedItem to return the string. | select to select the item with the specified position or string.
Label | getText to return the displayed text. | setText to set the displayed text.
List | getSelectedIndex to return the integer position of the selected item. getSelectedItem to return the string value of the selected item. getItem to return the string in the given position. | add to append/insert items. remove or removeAll to remove items. replaceItem to change an item. select to select an item.
MenuItem | getLabel to return the item text. | setLabel to set the item text.
TextArea or TextField | getText to return user-entered text. getSelectedText to return the selected text. | setText to change all text. select to select a text subset. selectAll to select all text. setCaretPosition to position the cursor. setEditable for read mode.
TextArea |  | append to add text. insert to insert text. replaceRange to replace text.

As a quick example, here is a line of code that reads an entry field, where the entry field is an object reference variable of type TextField called myEntry:

String theText = myEntry.getText();

You will often want to strip off the leading and trailing blanks, using the String class method trim:

String theText = myEntry.getText().trim();

Finally, when processing your OK push button in the actionPerformed method, you will be querying all the user-specified information on the screen to validate it. If it is all valid, and you want to exit your window, call the dispose method that you inherited from Frame. Otherwise, display an error message and just "return" from that method so the window remains displayed.

A Small AWT Example: A To-Do List

To pull all this together, we offer a simple little application that contains a list of to-do items, and buttons to add, change, and remove items. The Add and Change buttons bring up a secondary dialog where you add or change the text. To show menus, a menu bar is included with one menu, Edit, which contains add, change, and remove items that do the same thing as the buttons. Figure 12.22 shows the application and the Add and Change dialogs.

Click To expand
Figure 12.22: A simple to-do list application in AWT.

The main window is coded in ToDoList.java (Listing 12.18), which extends Frame, and includes the processing for the button and menu events. Both of these send Action-Events, so the ActionListener interface is implemented, calling addActionListener on the button and menu Item objects, and supplying an actionPerformed method to do the actual processing. Processing is also added for the double-click event in the list box, and equated to pressing the Change button. This is also an ActionEvent, so we only had to call add ActionListener on the list component. Finally, to disable the Change and Remove buttons unless a list item is selected, the ItemListener interface is implemented, calling addItemListener on the list component with an itemStateChanged method to handle the event. Of course, an instance of the FrameListener class is also used to handle window-closing events.

The Add and Change dialogs are actually one class (ToDoListEntry.java, shown in Listing 12.19) that extends Dialog and takes a Boolean parameter in the constructor, indicating if the user is in Add or Change mode. Of course, it also has to accept a parameter for the parent frame window, and for change mode, it needs the text of the item to be changed. In this class, the Add/Change button is processed by setting an instance variable to hold the contents of the entry field, and supplying the method getEntryText that the calling class can use to query it. Another Boolean instance variable, okButtonPressed, is set, and the method okPressed is supplied so the caller knows if the dialog was cancelled or not. For both buttons, dispose is called to hide the dialog.

Listing 12.18: The ToDoList class.
Start example
import java.awt.*;
import java.awt.event.*;

/**
* A simple example application that manages a list of ToDo Items.
* Demonstrates Event Processing in AWT.
*/
public class ToDoList extends Frame
                     implements ActionListener, ItemListener
{
   private List list;
   private Button addButton, changeButton, removeButton;
   private MenuBar menubar;
   private Menu    editMenu;
   private MenuItem addMenuItem, changeMenuItem, removeMenuItem;

   /**
   * Constructor
   */
   public ToDoList()
   {
      super("ToDo List");

      // create components
      list = new List(8); // display 8 rows maximum
      addButton = new Button("Add...");
      changeButton = new Button("Change...");
      removeButton = new Button("Remove");
      Panel buttons = new Panel();
      buttons.setLayout(new GridLayout(3,1));
      buttons.add(addButton);
      buttons.add(changeButton);
      buttons.add(removeButton);
      changeButton.setEnabled(false);
      removeButton.setEnabled(false);

      // create menubar
      menubar = new MenuBar();
      editMenu = new Menu("Edit");
      addMenuItem = new MenuItem("Add...");
      changeMenuItem = new MenuItem("Change...");
      removeMenuItem = new MenuItem("Remove");
      editMenu.add(addMenuItem);
      editMenu.add(changeMenuItem);
      editMenu.addSeparator();
      editMenu.add(removeMenuItem);
      menubar.add(editMenu);
      setMenuBar(menubar);

      // enable events
      addButton.addActionListener(this);
      changeButton.addActionListener(this);
      removeButton.addActionListener(this);
      addMenuItem.addActionListener(this);
      changeMenuItem.addActionListener(this);
      removeMenuItem.addActionListener(this);
      list.addActionListener(this); // when item is double clicked
      list.addItemListener(this); // so we know when items are selected

      // set layout, add components
      setLayout(new BorderLayout(5,5));
      add(list,"Center");
      add(buttons,"East");

      setLocation(200,200);
      pack();
      setVisible(true);

      // enable closing via 'x'
      FrameListener closer = new FrameListener(this,true);
      addWindowListener(closer);
   } // end constructor

   /**
   * Called by Java when button pressed or menu item selected
   */
   public void actionPerformed(ActionEvent evt)
   {
      Object part = evt.getSource();
   if ((part == addButton) ||
      (part == addMenuItem))
   {
      ToDoListEntry dlg = new ToDoListEntry(this,true,null);
      dlg.setVisible(true);
      if (dlg.okPressed())
      {
         list.add(dlg.getEntryText());
         int itemCount = list.getItemCount();
         list.select(itemCount-1);
         changeButton.setEnabled(true);
         removeButton.setEnabled(true);
      }
   }
   else if ((part == changeButton)   || // button
            (part == changeMenuItem) || // menu item
            (part == list))             // double click
      {
         int selectionIndex = list.getSelectedIndex();
         if (selectionIndex != -1)
         {
            String selectionText = list.getSelectedItem();
            ToDoListEntry dlg = new
               ToDoListEntry(this,false,selectionText);
            dlg.setVisible(true);
            if (dlg.okPressed())
            {
               list.replaceItem(dlg.getEntryText(), selectionIndex);
               list.select(selectionIndex);
            }
         }
         else
            changeButton.setEnabled(false);
      }
      else if ((part == removeButton) ||
               (part == removeMenuItem))
         {
            int selectionIndex = list.getSelectedIndex();
            if (selectionIndex != -1)
               list.remove(selectionIndex);
            changeButton.setEnabled(false);
            removeButton.setEnabled(false);
         }
   } // end actionPerformed

   /**
   * Called by Java when an item in the list is selected/deselected
   */
   public void itemStateChanged(ItemEvent evt)
   {
      int selectionIndex = list.getSelectedIndex();
      boolean enableButtons = (selectionIndex != -1);
      changeButton.setEnabled(enableButtons);
      removeButton.setEnabled(enableButtons);
   } // end itemStateChanged

   public static void main(String args[])
   {
      System.out.println("Welcome to Class ToDoList");
      ToDoList me = new ToDoList();
   }
} // end ToDoList class
End example

In processing ActionEvents for the Add and Change buttons (and the menu items) in the main ToDoList class, the ToDoListEntry class is instantiated with a call to setVisible to modally display it. That only returns control when the user exits the dialog. The okPressed method is then called to determine how the user exited, and if it returns true, getEntryText is called to get the text back and either append it to the list (with the add method) or change the current list item's text (with the replaceItem method). Note that the dialog objects that are created are swept up by the garbage collector when the actionPerformed method goes out of scope. The Remove button (and menu item) simply removes the selected item from the list using the remove method.

Listing 12.19: The ToDoListEntry class.
Start example
import java.awt.*;
import java.awt.event.*;

/**
* A dialog that pops up when user selects to Add or Change
* a todo list item in the ToDoList window
*/
public class ToDoListEntry extends Dialog implements ActionListener
{
   private TextField entry;
   private Button okButton, cancelButton;
   private boolean okButtonPressed = false;
   private String entryText = null;

   /**
   * Constructor
   * @param Parent frame window object
   * @param true if this is called in Add mode, false if Change mode
   * @param for change mode, the currently selected text
   */
   public ToDoListEntry(Frame parent, boolean addMode, String initialText)
   {
      super(parent, true);
      // create components
      if (addMode)
      {
         setTitle("Add ToDo Entry");
         okButton = new Button("Add");
      }
      else
      {
         setTitle("Change ToDo Entry");
         okButton = new Button("Change");
      }
      cancelButton = new Button("Cancel");
      Panel buttons = new Panel();
      buttons.setLayout(new GridLayout(1,2));
      buttons.add(okButton);
      buttons.add(cancelButton);

      entry = new TextField(30);
      if (initialText != null)
         entry.setText(initialText);
      Panel prompt = new Panel();
      prompt.setLayout(new FlowLayout());
      prompt.add(new Label("ToDo Item"));
      prompt.add(entry);

      // enable events
      okButton.addActionListener(this);
      cancelButton.addActionListener(this);

      // set layout, add components
      setLayout(new BorderLayout(5,5));
      add(prompt,"Center");
      add(buttons,"South");
      setLocation(300,100);
      pack();

      // enable closing via 'x'
      FrameListener closer = new FrameListener(this,false);
      addWindowListener(closer);
   } // end constructor

   /**
   *
   Called by Java when button pressed or menu item selected
   */
   public void actionPerformed(ActionEvent evt)
   {
      Object part = evt.getSource();
      okButtonPressed = (part == okButton);
      entryText = entry.getText().trim();
      if (okButtonPressed && (entryText.length() == 0))
         Toolkit.getDefaultToolkit().beep();
      else
         dispose();
   } // end actionPerformed

   /**
   * Called by caller after returning so they can
   * determine how the user exited the dialog
   */
   public boolean okPressed()
   {
      return okButtonPressed;
   }

   /**
   * Called by caller after returning so they can
   * extract the user's text entered in the entry field
   */
   public String getEntryText()
   {
      return entryText;
   }
} // end ToDoListEntry class
End example

Of course, the example in Listings 12.18 and 12.19 is not complete, since it doesn't save the list items to disk. You'll see how to save items to a DB2 database in chapter 13, and how to save to a local flat file in chapter 14.

Error Checking

In Java AWT (and Swing), the TextField component is basic—it accepts any string as input, period. It is up to you, then, to verify what the user has typed in. You have to write all this code yourself, unfortunately. We suggest you create a new class that extends TextField, and perhaps call it SmartTextField. You can place all your validity-checking code here, and then use objects of this field instead of TextField directly.

You have the choice of either verifying input as the user types it, using the Key-Listener interface, or supplying a verify method that verifies the input when called by the programmer (for example, when the OK button is pressed). We prefer the latter option because it is less disruptive to end users and does not impede performance. (Checking every keystroke can be slow.) Your new class might have methods, such as setDataType and setMaxLength, that supply information to help your verify method check for errors.

Your verify method will read the contents of the entry field, left- or right-justify it with or without padding, as desired, and then ensure that the contents are valid for the data type, length, decimals, and so on, as specified via the setXXX methods. The justified output should be re-sent to the screen for visual update. If there is an error, you should display it in an error-message box or in the TextArea message line, if there is one (supply a setMsgLine method for identifying this).

To help verify input, you could look at the DecimalFormat class in the java.text package, or simply write your own. For example, to pad and justify an integer data type, you might have a method like the one in Listing 12.20.

Listing 12.20: A method to pad and justify an integer data type.
Start example
public static final String padInteger(int intnum, int finalLen)
{
   String paddedString;
   paddedString = Integer.toString(intnum);
   int curLen = paddedString.length();
   if (curLen < finalLen)
   {
      StringBuffer temp = new StringBuffer(finalLen);
      int padAmount = finalLen—curLen;
      int i,j;
      for (i=0; i < padAmount; i++)
         temp.append('0');
      for (j=0; i < finalLen; i++,j++)
         temp.append(paddedString.charAt(j));
      paddedString = temp.toString();
   }
   return paddedString;
}
End example

To verify the padded and justified number, you might have a method as in Listing 12.21.

Listing 12.21: A method to verify the padded and justified number from Listing 12.20.
Start example
public static final int verifyInteger(String stringInt, int min, int max)
{
   int returnVal = -1;
   try
   {
      Integer intObj = Integer.valueOf(stringInt);
      returnVal = intObj.intValue();
      if ((returnVal < min) || (returnVal > max))
         returnVal = -2;
   }
   catch (NumberFormatException e) {}
   return returnVal;
}
End example

If you do detect an error, you might want to change the foreground and background colors of the entry field, which you know how to do. Just be sure to reset them again as the first thing you do in the verify method. Also, if there is an error, you should do a com ponent.requestFocus call to set the focus on the entry field being verified. You might put these methods in their own class, such as StringMethods.

By the way, VisualAge for Java Enterprise Edition contains, in its Enterpise Toolkit for OS/400, two very useful Java Beans in its AS/400 palette, called AS400VisualTextField (for AWT) and JFormattedText (for Swing). They do all of this and more: data type, length, decimal, range and value error-checking, edit-masking (e.g., 123 can become 123.45 when the field is not in focus), and automatic cursor advance after the last digit is typed. They are perfectly usable by OS/390 programmers as well, despite where they are packaged.

Error Messages

Having written code to verify the user's entry field input, you must now display an error message if it is wrong. On OS/390 and OS/400, of course, you use messages defined in a message file, with substitution values supported. In Java, you work with String objects. Your first decision must be how to deal with substitution variables. Presumably, you want to create a number of generic error messages like you do on OS/390 and OS/400, and substitute the particular values on the fly. The String class, unfortunately, does not support replacing substrings with other substrings, so you have two choices:

  1. Use the MessageFormat class in the java.text package. This allows you to specify in the constructor a string message with substitution variables defined between curly braces, as in "File {0} not found." The substitution values are supplied as an array of Java objects (usually strings), and the number between the curly braces is used as the index into that array.

  2. Write your own substring-substitution static method class. Listing 12.22 shows how you might do this.

    Listing 12.22: A substring-substitution static method class.

    Start example

    public static String sub(String msg, String subOld, String subNew)
{
   StringBuffer temp = new StringBuffer();
   int lastHit = 0;
   int newHit = 0;
   for (newHit = msg.indexOf(subOld,lastHit); newHit != -1;
      lastHit = newHit, newHit = msg.indexOf(subOld,lastHit))
   {
      if (newHit >= 0)
         temp.append(msg.substring(lastHit,newHit));
      temp.append(subNew);
      newHit += subOld.length();
   }
   if (lastHit >= 0)
      temp.append(msg.substring(lastHit));
   return temp.toString();
}
    End example

The class in Listing 12.22 will substitute all occurrences of a given substring with a replacement string, returning the resulting "final" string. For example, you might define error-message string constants of the following form:

public static final String MSG_FILENOTFOUND = "File &1 not found.";

You could then call the method sub with the contents of an entry field with this code:

msg = sub(MSG_FILENOTFOUND, "&1", filenameEntry.getText().trim());

In either case, you finally have a String object with the required substitution performed. How, then, do you display it to the end user? Unless the message requires a user response (such as "File &1 already exists. Replace it?"), we recommend you display it in a TextArea component at the bottom of your window. You might want to change the colors of that area to be white characters on a red background when displaying an error message, in order to attract the user's attention.

If you need to ask the user a yes/no question, or you want to force the user to read the message, you will need to define a new class for popup error messages. It will have to use a Dialog class because you do not want your program to regain control after showing it until the user has answered the error (via Yes, No, or OK buttons). Thus, you want the modal support offered by the Dialog class.

You should supply a constructor that takes, as input, the Frame parent, the message to show, and whether or not to show Yes/No buttons (Boolean). You can then easily show the message in a TextArea read-only component in the Center area of a flowed layout panel, and the Yes/No or OK buttons in the South area. The actionPerformed method should set an instance variable, buttonPressed, to an integer constant, and then dispose the dialog. You should supply a getButtonPressed method as well, to retrieve that value. (This sequence of steps is too big to show here, but if you are interested, look at the MsgBox.java class on the supplied CD-ROM.)

When you switch from AWT to Swing, life is much better! Swing supplies a very flexible message dialog where you can choose the buttons to show (such as OK/Cancel or Yes/No), the icon to show, and the contents of the message (even the component). See JOptionPane in the javax.swing package for details.

APPLETS VERSUS APPLICATIONS

Recall the discussion on Java applets versus Java applications from chapter 2:

  • Applications can be invoked from a command line, using java MyClass. This first class requires the existence of the following, which the Java runtime looks for:

    public static void main(String args[]) {...} method

  • Applets cannot be invoked from the command line. They must be embedded inside an HTML Web page, using an APPLET tag to refer to them. The Web browser, on encountering an APPLET tag, will retrieve the applet from the host server where the Web page came from and run it.

All of the examples to this point have used an application-style main method and have been invoked from the command line for testing purposes. All of the "non-main" code is still valid for applets; they use the same Java language and Java-supplied packages as Java applications do. However, there are some restrictions on applets, related to security.

Java applets are, by default, restricted to a sandbox that prevents them from accessing the workstation or personal computer the Web Browser is running on, or any server other than the one they came from. Applets, then, cannot do any of the following:

  • Access local files in any way, including running local programs.

  • Access or change local properties, like the current working directory.

  • Access any server except the one they came from, via any form of communications.

These restrictions are enforced by the Web browser, although most browsers allow these restrictions to be configured. Further, Java includes the capability of digitally signing your applets using the JDK-supplied JavaKey tool. Although we will not discuss this tool in detail here, it allows for mutually consented use of a Java applet that has full access rights. It ensures the user of the applet that the applet came from the programmer and has not been tampered with since, and it ensures the programmer of the applet that only those people or "entities" identified are allowed to run it.

Applets and AWT

If Java applications require the existence of a main method to be invoked from the command line, what do applets require to be invoked from a Web browser? The answer is this:

Applets are classes that extend the java.applet.Applet class.

You create an applet by creating a new class that extends Applet. What is so special about this Applet class? Actually, not too much. The java.applet.Applet class itself extends the java.awt.Panel class. You have already seen the Panel class—you used it to produce self-contained and easily reusable (encapsulated) window "parts" containing components and a layout manager to manage their appearance.

It makes sense that an applet would extend, or be a panel, then, because the Web browser already supplies the Frame window, and the applet is simply staking out a part of the real estate of that window. This is what panels do, as well. What the Applet class does beyond the Panel class is manage the interaction between your applet and the Web browser, which is the host.

To create an applet, start with a fresh, new class:

public class MyFirstApplet extends java.applet.Applet
{
   ...
} // end class MyFirstApplet
Note

Applet classes must be public.

You will also supply method overrides to four key methods from the Applet class, which the Java runtime will call automatically at the appropriate times in an applet's life. These are shown in Table 12.11.

Table 12.11: Four Methods to Override in Applets.
Method | Description
public void init() {...} | Called once when the applet is first loaded to do initialization of variables and state. Equivalent to a constructor.
public void start() {...} | Called after init at applet load time, but also called to restart a resting applet after the user surfs away and subsequently returns to this Web page. Equivalent to a COBOL program's "main line" code. Typically, it restarts threads and possibly restores state.
public void stop() {...} | Called when the user surfs away to another Web page. Typically, it suspends threads.
public void destroy() {...} | Called just before the browser exits, to give you a chance to clean up anything, like canceling any threads and closing any files or database connections.

You do not have to override all of the methods in Table 12.11 with those of your own, although most robust applets will. The minimum you need is simply init.

Let's now convert the TestEvents example (from earlier in this chapter) from an application Frame class to an applet. Have a look at Listing 12.23. The commented-out lines of code are those that are not applicable to an applet, or have been changed in the subsequent line of code.

Listing 12.23: Converting a Frame window class to an applet class.
Start example
import java.awt.*;
import java.awt.event.*;
//class TestEvents extends Frame
public class TestMyApplet extends java.applet.Applet
{
   private CompanyPromptWithEvents companyprompt;
   private TextArea      msgLine;
   //TestEvents()
   public void init()
   {
      //super("My First Java Window");
      //setSize(300,200);
      //setLocation(200,100);
      setBackground(java.awt.Color.lightGray);
      msgLine       = new TextArea("msg line",1,1,
         java.awt.TextArea.SCROLLBARS_HORIZONTAL_ONLY);
      msgLine.setEditable(false); // read-only
      sgLine.setForeground(java.awt.Color.red);
      companyprompt = new CompanyPromptWithEvents(msgLine);
      setLayout(new BorderLayout(5,5));
      add(companyprompt, "Center");
      add(msgLine,       "South");
      //pack();
      //setVisible(true);
   }
}
End example

Was that simple enough? We just changed the class definition to be public, extended Applet instead of Frame, changed the constructor method to init, and commented out any Frame window-related method calls. Not bad. Plus, the CompanyPrompt class was used, verbatim! All the input processing applies as-is to applets, but pack is not allowed for applets. Figure 12.23 shows what this applet looks like in Netscape.

Click To expand
Figure 12.23: An applet running in Netscape.

To get the class to show up in a Web page, as in Figure 12.23, a snippet of HTML source is needed. It is shown in Listing 12.24 (which is in the file TestMyApplet.htm on the CD-ROM).

Listing 12.24: The HTML source to embed an applet.
Start example
<html>
<head>
<title> My First Java Applet! </title>
</head>
<body>
<p> Well, here it is...
<br>
<APPLET CODE="TestMyApplet.class" WIDTH=300 HEIGHT=200>
</APPLET>
</body>
</html>
End example

The APPLET tag is what is important here. With it, you identify your applet class via the CODE= parameter, and the size of your applet's panel via the WIDTH and HEIGHT parameters. These are equivalent to java.awt.Frame's setSize method. There are many more possible parameters to the APPLET tag, such as CODEBASE to identify a subdirectory where your applet class files are stored relative to the Web page's HTML files. For information on this tag, consult the documentation for the JDK, in the file \docs\guide\misc\ap-plet.html of your JDK subdirectory.

Graphics

Java includes rich graphics support in the java.awt.Graphics and java.awt.Canvas classes. However, these are not often used in business applications such as yours, and are covered in many other books, so they are not covered here.

If you need to draw a polygon, rest assured that you can. More likely, though, you will want to display an image, and the java.awt.Image class is intended to do just that. Actually, to display images, you usually use the new ImageIcon class in Swing (in the javax.swing package). You simply specify the .gif or .jpeg file name in the constructor, and it will load it in from disk. You can then use this object inside Swing labels, lists, menus, and buttons. The Applet class method getImage makes retrieving the image from your server relatively simple.

Locales

The main idea behind international support is to allow your program to work in different languages automatically. The trick is to define all your language-sensitive and country-sensitive information (for example, currency is different per country, while language might not be) in a class that is somehow tagged as being for a particular language and/or country. Then, for each language and/or country in which you want your program to operate correctly, you simply supply a unique version of this class. For Internet applets, this is especially important, given the international access your applet will have.

At runtime, then, you need to somehow identify the language and/or country in which your user works. This can be done by asking him or her directly via the user interface (for example, through language- and country-choice dropdowns). It can also be done by simply using the Java system default, which is set up to be the same as the user's computer, Web browser, or other host in which the Java application or applet is running. Having determined the language and country pair (or optionally, either a language or country, using a default for the other), your program will choose the appropriate class described earlier, containing language/country-specific information, such as translatable strings. How does it do this? Let's look a little deeper.

In Java, the current operating-language/country mode is captured by a class called Locale in the java.util package. You create a new Locale object by specifying in the e constructor the language and the country, as strings. One of these strings can be null, if you are not interested in country-specific languages or language-specific countries. The string values are two-character codes that are predefined by an ISO (International Standards Organization) standard. However, you need not worry about them; rather than instantiating locales, you will probably just use the predefined "constant" locales supplied as helpers in the Locale class, such as Locale.US, Locale.GERMANY, Locale.GERMAN, and so on.

What are these mysterious locales, and why do you need them? A number of classes in the java.text package for such things as message, date, and number formatting allow you to pass in one of these locale objects so the formatting is done correctly for that locale. These methods are called locale sensitive. They are intended to work correctly for any given locale, or at least the locales they claim to officially support. For example, you can ensure that the appropriate date format and decimal-point symbol are used. If no locale is specified, the system-default locale is used, which you can always query by calling Locale.getDefault.

To warm up your fingers for more coding to come, a simple little locale program is shown in Listing 12.25.

Listing 12.25: Writing default locale information to the console.
Start example
import java.util.*;
public class TestLocale
{
   public static void main(String args[])
   {
      Locale defloc = Locale.getDefault();
      System.out.println("name is    : " + defloc.getDisplayName());
      System.out.println("language is: " + defloc.getLanguage());
       System.out.println("country  is: " + defloc.getCountry());
   }
}
End example

On our Windows NT system, this provides the following results:

name is    : English (United States)
language is: en
country  is: US

Suppose you want to design your own locale-sensitive classes. The tricky thing to realize here is that Locale objects are quite useless on their own, since they hold no real information. They are simply objects that your code (and the Java-supplied code in java.text) can use as a key as to which language to display, or which language/country to support.

Let's write a locale-sensitive version of the simple BorderLayout version of "My First Java Window." The class constructor needs to be changed to take an optional Locale object, and display the window in the language for that locale. If it is not a locale into which our strings have been translated, the window will simply appear in English. Because the only translated language is German in this example, this will often be the case.

The first thing to do is extract all the strings from the program and put them in their own class, in a two-string array, where the first string is a key or identifier for finding the second, translated string. This way, the program will extract the strings by reading them from the array using that index. Java has built-in support for this in the form of the ListResourceBundle class. This class is extended to create the class in Listing 12.26.

Listing 12.26: The ResourceBundle subclass containing all translatable strings.
Start example
import java.util.*;
public class MyStrings extends ListResourceBundle
{
   public Object[][] getContents()
   {
      return contents;
   }
   static final Object[][] contents =
   {
      {"TITLE", "My First Java Window"},
      {"INSTR", "Enter company number"},
      {"NUMBER","Number"},
      {"LISTPB","List..."},
      {"OKPB",  "Ok"}
   }; // end contents array
}
End example

The getContents method is required to be overridden from the base ListResource-Bundle class because it is defined as abstract. It returns the array of translatable strings, an array called contents in this example.

Now, let's create a German version of this class. In Listing 12.27, all the strings are translated into German, and the name of the class is changed slightly to indicate that it is for German locales (with the suffix _de for Deutsche).

Listing 12.27: The translated ResourceBundle subclass containing translated strings.
Start example
import java.util.*;
public class MyStrings_de extends ListResourceBundle
{
   public Object[][] getContents()
   {
      return contents;
   }
   static final Object[][] contents =
   {
      {"TITLE", "Mein Erstes Java Fenster"},
      {"INSTR", "Firm nummer eingeben"},
      {"NUMBER","Nummer"},
      {"LISTPB","Liste..."},
      {"OKPB",  "Ja, Das ist Gut!"}
   }; // end contents array
}
End example

The code needs to be changed to use these strings, and the appropriate version of the class, as opposed to hard-coding the strings. You can do this by using the getBundle static method in the ResourceBundle class to return the appropriate version of the class. When you give this method the base name of the ListResourceBundle child class ("MyStrings") and a locale object, it loads the appropriate class for that locale. With this ResourceBundle reference (a parent class of the MyStrings class, so by polymorphism it can point to a MyStrings object at runtime), you can extract strings from it using the method getString, which takes the string key defined for each string. Let's have a look, in Listing 12.28.

Listing 12.28: Converting hard-coded strings to ResourceBundle retrieved strings.
Start example
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class TestTranslated extends Frame
{
   // define GUI components
   private Label     instruction, prompt;
   private TextField entry;
   private Button    listButton, okButton;
   private ResourceBundle strings;
   // Constructor
   TestTranslated(Locale locale)
   {
      super();
      strings = ResourceBundle.getBundle("MyStrings",locale);
      setTitle(strings.getString("TITLE"));
      setSize(300,200);
      setLocation(200,100);
      setBackground(java.awt.Color.lightGray);
      // instantiate components
      instruction = new Label(strings.getString("INSTR"));
      prompt      = new Label(strings.getString("NUMBER"));
      entry       = new TextField(5);
      listButton  = new Button(strings.getString("LISTPB"));
      okButton    = new Button(strings.getString("OKPB"));
      // set window layout manager
      setLayout(new BorderLayout(5,5));
      // add components to window
      add(instruction,"North");
      add(prompt,     "West");
      add(entry,      "Center");
      add(listButton, "East");
      add(okButton,   "South");
      // show the window
      pack();
      setVisible(true);
   } // end constructor
} // end TestTranslated class
End example

That's it; the class is now locale-sensitive. It would be good form to also supply a second constructor, such as this one:

// Constructor 2
TestTranslated()
{
   this(Locale.getDefault());
}

You can now invoke this class with a German locale, as follows:

TestTranslated test = new TestTranslated(java.util.Locale.GERMAN);

This produces the expected German results, shown in Figure 12.24. If you call it with any other locale, or none at all (if you coded in that second constructor), you get the default English version.

Figure 12.24: A translated application.

To add new languages, simply copy and rename the MyStrings class to MyStrings_xx, where xx is the appropriate two-character ISO language code (for example, fr for French, es for Spanish, it for Italian, ja for Japanese, and pt for Portuguese). You can get these codes from your Locale class documentation. The name is important because the getBundle method that you call to find the correct class tacks on the appropriate suffix to the given base name, for the given locale. Thus, supplying it with a MyStrings base name and a German locale sends it looking for the class MyStrings_de, which it finds. If it does not find the class (after trying a couple of other variations involving the country code of the locale), it looks for the base class, MyStrings. This, then, is your default for unsupported locales.

We recommend that you always put all of your potentially translatable strings inside ListResourceBundles like this, rather than hard-coding them as you go. It is much more difficult to go in after the fact and find all the strings than it is to start correctly in the first place.

There is more to supporting internationalization than simply factoring out your translatable strings, as you probably know—small details like date formats, postal codes, currency, and so on. You can learn about all of this in your JDK documentation as you need to, especially in the classes in the java.text package. For comprehensive information on the topic, look at the \docs\api\internat\Index.html file of your JDK directory.


A Brief Swing

The following is a little introduction to Swing, since you might want to know enough about it right now to start trying it on your own. This is a very brief overview of the features and the class names, but it's enough to get you going, together with the JDK documentation. As you read about Swing, you will see that is good that you had the introduction to AWT.

Swing is part of a group of packages and classes that are referred to as Java Foundation Classes, or JFC. The pieces of JFC are as follows:

  • AWT, or "GUI classic" for basic GUI development.

  • Swing (not an acronym) for very rich GUI development. See \docs\guide\swing\Index.html for a good starting point in learning Swing.

  • Two-dimensional graphics (2D) for working with line art, text, and images. In your JDK directory, see the \docs\guide\2d\Index.html file for details.

  • Accessibility for enabling assistive technologies like screen readers, speech recognition systems, and Braille terminals. In your JDK directory, see \docs\guide\accessibility\Index.html for details.

  • Drag-and-drop support for data transfer (usually, but not limited to, mouse drags). In your JDK directory, see \docs\guide\dragndrop\Index.html for details.

  • The Input Method Framework for allowing text components to receive Japanese, Chinese, and Korean input via input methods (single characters created from multiple keystrokes). In your JDK directory, see \docs\guide\intl\Index.html for details.

To begin the overview of Swing, Table 12.12 compares it to AWT. As you can see, the big differences are new package names, adding J to most component names, and changing code that uses add to insert components into a window to instead call getContentPane first, and then call add on the result. Events, colors, and layout managers from AWT are still used in Swing.

Table 12.12: Swing Versus AWT.
Comparing | AWT | Swing
Package names | java.awt and java.awt.event. | com.sun.java.swing and com.sun.java.swing.event for JDK 1.1.x, or javax.swing and javax.swing.event for 1.2.0 or higher. java.awt and java.awt.event are still used, as-is.
Windows and panels | Frame, Dialog, and Panel. | JFrame, JDialog, and JPanel in javax.swing.
Applets | Extend Applet in java.applet. | Extend JApplet in javax.swing.
Adding components to a panel | The add method for Panel. | The add method for JPanel.
Adding components to Frame or Dialog | The add method | The getContentPane(l) .add method for JFrame or JDialog.
Events | Interfaces in java.awt.event | Same, plus new ones for new components, in javax.swing.event.
Layout managers | BorderLayout, FlowLayout, GridLayout, GridBagLayout, and CardLayout. | Same, plus BoxLayout from javax.swing. This is a row or column of individually sized cells.
Colors | Color and SystemColor. | Same.
Button component | Button. | JButton.
Checkbox component | Checkbox. | JCheckBox. (Note the uppercase B.)
Radiobutton component | Checkbox plus CheckboxGroup. | JRadioButton plus ButtonGroup.
Text constant | Label. | JLabel.
Text-entry components | TextField and TextArea. | JTextField, JTextArea, and JPasswordField.
Dropdown | Choice | JComboBox.
Simple list | List. | JList.
Menus | MenuBar, Menu, MenuItem, CheckboxMenuItem, PopupMenu, and the setMenuBar method. | JMenuBar, Jmenu, JMenuItem, JCheckBoxMenuItem, JRadioButtonMenuItem, JPopupMenu, and the setJMenuBar method
Menu shortcuts | MenuShortcut. | The setAccelerator method of JMenuItem.

There is another difference between Swing and AWT that is not captured in Table 12.12: while AWT text areas and list boxes get scrollbars automatically, in Swing you have to put these inside JScrollPane objects in order to get scrollbars. Beware of that! Also, note that the BoxLayout layout manager comes with a Box class, which is a container (like JPanel) that is already set up with the BoxLayout manager, and has static factory methods for creating vertical and horizontal boxes (panels).

Listing 12.29 is a little Swing program that uses each of the basic components from Table 12.12.

Listing 12.29: A Java window class to show each of the basic Swing components.
Start example
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TestAllSwing extends JFrame
{
   JButton      button1 = new JButton("Cancel");
   JCheckBox    cbFish  = new JCheckBox("Fish",true);
   JRadioButton rb1 = new JRadioButton("Option 1", true);
   JRadioButton rb2 = new JRadioButton("Option 2");
   ButtonGroup  rbg     = new ButtonGroup();
   JComboBox    dropdown= new JComboxBox();
   JLabel       prompt1 = new JLabel("Enter name");
   JList        listbox;
   JTextField   entryName = new JTextField("yourname", 10);
   JTextArea    entryCmts = new JTextArea("comments", 5, 1);

   public TestAllSwing()
   {
      rbg.add(rb1);
      rbg.add(rb2);
      dropdown.addItem("choice 1");
      dropdown.addItem("choice 2");
      dropdown.setSelectedIndex(0);
      String itemArray[] = {"item 1", "item 2"};
      listbox = new JList(itemArray);
      listbox.setSelectedIndex(0);
      listbox.setPreferredSize(listbox.getMinimumSize());
      JScrollPane jsp1 = new JScrollPane(listbox);
      jsp1.setVerticalScrollBarPolicy
         (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
      JScrollPane jsp2 = new JScrollPane(entryCmts);
      jsp2.setVerticalScrollBarPolicy
         (JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
      jsp2.setHorizontalScrollBarPolicy
         (JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

      Box line1 = Box.createHorizontalBox();
      line1.add(prompt1); line1.add(entryName);
      Box line2 = Box.createHorizontalBox();
      line2.add(cbFish);  line2.add(rb1); line2.add(rb2);
      Box line3 = Box.createHorizontalBox();
      line3.add(jsp1);    line3.add(dropdown);
      Box line4 = Box.createHorizontalBox();
      line4.add(jsp2);
      BoxLayout layout =
         new BoxLayout(getContentPane(),BoxLayout.Y_AXIS);
      getContentPane().setLayout(layout);
      getContentPane().add(line1); getContentPane().add(line2);
      getContentPane().add(line3); getContentPane().add(line4);
      JPanel buttons = new JPanel(new GridLayout(1,1));
      buttons.add(button1);
      getContentPane().add(buttons);
      pack(); setVisible(true);
   } // end of ctor for TestAllSwing
} // end class TestAllSwing
End example

Sorry about squishing multiple statements on some lines. We don't want you doing this, but we wanted you to see all the code in as short a space as possible. Notice that for JList, you can now specify an array of items in the constructor call to populate the list with. They can be Strings, or ImageIcon objects for a list of pictures. This is a nice feature; JComboBox, JLabel, JMenu, JMenuItem, and JList (among others) allow it, too, via setImage methods.

Figure 12.25 shows what happens when the class from Listing 12.29 is instantiated. You probably notice that it looks a bit different than Figure 12.2 for AWT. This is because Swing is written from scratch in Java, and does not call the underlying operating-system APIs. Therefore, it will look and behave identically on all operating systems. This is great for testing. Another great thing about Swing is you can choose what the look and feel will be, programmatically. Figure 12.25 illustrates the "Metal" (default) look and feel. If you want it to look and feel like Windows, you could add the following lines of code before showing your window (or after, if you let the user choose via a menu option):

try {
   UIManager.setLookAndFeel(
      com.sun.java.swing.plaf.windows.WindowsLookAndFeel);
   SwingUtilities.updateComponentTreeUI(this);
} catch (Exception exc) {}

Figure 12.25: The basic Swing components.

To get a UNIX look and feel, use MotifLookAndFeel instead of WindowsLookAndFeel. Figure 12.26 shows the Windows look and feel.

Figure 12.26: The Windows look and feel.

This example does not look too different from the default look and feel, but some of them really do. We leave the Motif look and feel for you to try. There was rumor of a Macintosh look and feel, too, but as of this writing it is not available. Further, anyone can write their own look and feel in Swing.

You probably want to let your users decide on their preferred look and feel. To do this, use the following to query the installed look and feel options:

private UIManager.LookAndFeelInfo UIs[] =
          UIManager.getInstalledLookAndFeels();

Then, use getName on each element of the array to get an English name like "Windows" for a menu item or radio button, and use getClassName to get the input needed by setLookAndFeel to change to that look and feel.

Swinging Harder and Faster

There is much more to Swing than the basics shown so far (which are more or less the same as AWT). It is this advanced stuff that really gives Swing its power and popularity. We won't burden you with the details of all this now; some books dedicated to Swing are over 1,000 pages long in themselves. To give you an idea of what you can look forward to, though, Table 12.13 summarizes the advanced support that Swing brings to the table.

Table 12.13: Advanced Functions in Swing.
Functionality | Comment
The setMnemonic method | For setting a visually underscored character in any component that has text or a label. Users can use Alt plus this character to select that component quickly.
The setToolTipText method | Sets hover help ("fly over") for a component.
The setBorder method | Sets a visual border for a component. There are many to choose from.
The AbstractAction class | Specify in one place the label text, the icon, and the code to execute for particular actions. Assign these to menu items, toolbar items, and pushbuttons for easy reuse of redundant actions.
Images everywhere | You can put gif and jpeg images (IconImage objects) in labels, buttons, checkboxes, radiobuttons, menu items, lists, and dropdowns.
JToolBar | Toolbars usually go below the menu bar. In Swing, the user can tear these off and dock them anywhere. Cool!
JProgressBar | A visual indicator of progress so far.
JSlider | A GUI slider for a large range choices.
JTree | A tree view much like Windows Explorer's left side.
JTable | A multiple-column list, much like Windows Explorer's right side and OS/400 subfiles.
JTextPane | A very advanced, multiple-line editor with support for rich text, such as various colors, fonts, etc.
JEditorPane | A specialized editor for displaying/editing HTML or RTF.
JSplitPane | For creating split windows with movable horizontal or vertical bars.
JTabbedPane | For creating "wizards" or "notebooks" with multiple windows, each with their own tab.
JColorChooser, JFileChooser, JOptionPane | Pre-built windows for quick reuse. Color dialog, File dialog, and message/user-prompt dialog.
JDesktopPane and JInternalFrame | For Multi-Document-Interface (MDI) applications. A master window with multiple windows scoped inside it.
model/view separation | Allows you to separate the contents "data or model" from the UI component "view" for all components in Swing. Every component class has a corresponding model class that holds the data. This is created for you by default, but you can create your own instance, populate it, and then attach it (with setModel) to one or more components. If there are more than one, changes in the model data are reflected in all attached components.

A Small Swing Example: A To-Do List

Figure 12.27 shows the previous AWT sample application from Figure 12.22, redone as a Swing application. The sample is embellished a bit by adding a new look and feel menu, so you can play with changing the "L&F" to see the effects. We also got rid of our own dialog class (ToDoListEntry.java) and replaced it with a simple call to JOptionPane. showInputDialog, which creates the fully functional dialog you see. We just supplied the title, prompt text, and initial text as parameters.

Click To expand
Figure 12.27: The to-do list sample redone in Swing.

The question-mark graphic was supplied by Swing because we specified QUESTION_ MESSAGE for one of the parameters. You can change it to PLAIN_MESSAGE to get rid of the graphic. We also added borders to the push buttons, menu bar, and menus to make them stand out a bit more. Finally, we added mnemonics (underlined letters so power users can select the menu item via Alt plus the letter) to each of the menus, using the setMnemonic method.

It is very painful picking unique letters for each menu item within a menu for mnemonics, so we wrote a little static method, addMnemonicsForMenuBar, in another class we supplied, Mnemonics, which automates that task. Given the JMenuBar object, it walks through the menu items, picks the first unique letter from each item's text, and assigns that letter as the mnemonic. There are other non-static methods in the class to do the same for radio buttons, check boxes, buttons, and more. This class is a keeper if you do any Swing programming.

We will not show the code here, since it is fairly lengthy, but you can look at it in the ToDoListSwing.java file on the CD-ROM if you are interested. In the file, all of the original AWT code that was changed is shown commented-out for context. There are some big changes, such as having to use a DefaultListModel class for adding and changing the list box contents, using MouseListener instead of ActionListener for double-click events in the list, and using ListSelectionListener instead of ItemListener for selection events in the list. Also, the list methods for querying selection, setting selection, adding items, changing items, and removing items are all changed for Swing. If you do any list-box coding in Swing, this is a good sample to refer to.

Note

The to-do list example in Swing is written for JDK 1.2.0 or above. If you are still using JDK 1.1.x, change the import statements from javax.swing.* and javax.swing.event.* to com.sun.java.swing.* and com.sun.java.swing.event.*.

Swing Wrap

If you are going to write graphical user interfaces, instead of, say, HTML user interfaces, we highly recommend Swing over AWT. Whether you use AWT or Swing, we recommend you use a WYSIWYG tool like VisualAge for Java's Visual Composition Editor (VCE) to make life easier for yourself. Bear in mind that to use this or any tool effectively, you still need an underlying understanding of the Java GUI functionality. For example, these tools don't help you choose a layout manager; they only enable you to easily set up the one you decide on.

Now, to reward your hard work in reading to the end of this big chapter, we give you a nice class that you will probably use a lot in your GUI coding. Remember, we said that TextField and JTextField offer nothing in terms of error-checking? Even if you set the maximum characters via their constructors, this is only used for display-size calculations, and not enforced. So, we give you an intelligent entry field class called SmartTextField on the CD-ROM.

SmartTextField is not covered in detail here, but we suggest you check it out. It extends Java's JTextField to restrict users from typing more than the maximum characters specified in the constructor. It also restricts input to numeric digits, and it auto-advances the cursor when the maximum number of digits are typed. Also, it formats the contents when the entry field does not have focus. By default, this involves using a dollar sign and a decimal point at the two-digit location, but you can change that via set method calls.

To try SmartTextField, run the TestSmartTextField class supplied with it. Try typing nonnumeric characters. Then type digits, and watch what happens after typing the seventh digit (which is the maximum limit specified). Enjoy!


Summary

This chapter helped you learn the following:

  • What the Java Abstract Windowing Toolkit package (java.awt) offers in terms of components and layout managers.

  • How to process input (events) in Java.

  • How to write Java applets versus applications.

  • How to write translatable user interfaces in Java.

  • The similarities and differences between AWT and Swing, and a list of the advanced functionality that Swing offers over AWT.

This chapter also supplied a small sample application, first in AWT, then in Swing. You are now ready to get GUI'ing!


Chapter 13: Database Access

By this chapter, you have been exposed to much of the Java language, and should be getting comfortable with the Java syntax and capabilities. However, the primary activity in your COBOL code is accessing your data. Almost all COBOL applications revolve around database access. They are "based" on "data." Clearly, then, in order to put Java to use-either on the client or on the server-you need to know how Java programs access database data.

Accessing Data From COBOL

Describing data access from COBOL for both OS/390 and OS/400 is a diverse topic. That is because there are many options for storing, and hence accessing, that data.

Data on OS/390

OS/390 has underlying partitioned data sets, but on top of that, there are numerous options, including these:

  • BSAM (Basic Sequential Access Method).

  • QSAM (Queued Sequential Access Method).

  • VSAM (Virtual Sequential Access Method).

  • IMS (Information Management System) DB for OS/390.

  • DB2 for OS/390 (recently renamed to DB2 UDB Server for OS/390).

  • Other competing relational database offerings, such as Oracle and Natural.

The xSAM access methods are record-oriented and pre-date the relational databases, IMS DB and DB2. IMS DB pre-dates DB2. All three are heavily used and contain huge volumes of data running some very important businesses-like yours.

DB2 on OS/390 is part of the DB2 family that includes DB2 on OS/400, Windows, OS/2, AIX and many flavors of UNIX, including Linux. All support the same advanced technology combined with industry-standard access methods, including SQL (Structured Query Language) statements and CLI (Call Level Interface) APIs (Application Programming Interfaces).

Refer to the Web site www.ibm.com/software/data for information about the DB2 family, especially a white paper there titled Celebrating IBM Data Management, written by Charles Bontempo of the Polytechnic University. It offers a warm-hearted review of IBM's long history in databases on OS/390 and OS/400 (such as the invention of relational databases and SQL), and some exciting recent enhancements.

Data on OS/400

OS/400's data access is an easier topic than OS/390, since there is only one datastore, the DB2/400 database (recently renamed to DB2 Universal Database for AS/400). It can be accessed using record-level access from native COBOL, or using embedded SQL. The underlying database is the same in both cases, and is built into the operating system, not an optional add-on.

DB2/400 databases can be created using DDS (Data Description Specifications) source compiled into physical files (containing data) or logical files (containing alternate views or indexes of physical files). The terminology used for this traditional view is file for the physical or logical file, record for a row of data, field for a column of data within a row, and record format for the field descriptions of a single row. These files are placed in libraries, often together with the programs that manipulate them.

DB2/400 databases can also be created with industry-standard SQL, either from a high-level language or from the Interactive SQL utility. The result is the same as, and interchangeable with, DDS-authored files. The equivalent terminology for SQL is table versus physical file, index versus keyed logical file, view versus non-keyed logical file, row versus record, column versus field, schema versus record format, and collection versus library. Note that schema can also mean the combination of the library together with the files (tables), catalog, and indexes in it.

Data and COBOL

While the topic of data in COBOL is large, the discussion can be simplified by grouping data access into the following areas:

  • Direct, record-level access using COBOL syntax defined in the COBOL language. For OS/390, this allows direct access to QSAM and VSAM files. For OS/400, this allows direct access to DB2/400 files.

  • SQL access to relational data using embedded SQL statements. For both operating systems, this allows access to DB2 using industry-standard SQL statements that are reasonably standard from database vendor to database vendor. For example, on OS/390, you could also use this technique to get to an Oracle database. These are preprocessed into COBOL statements (mainly database API calls).

  • Transaction monitor access, such as CICS (Customer Information Control System) TS or IMS TM, using embedded transaction-monitor statements. For CICS, sometimes this is combined with embedded SQL for DB2 access, or with CICS-supplied statements for VSAM access (on OS/390), or EXEC DL/I (Data Language) statements for IMS TM to IMS DB.

  • API access to data. For example, DB2 supplies CLI APIs as an alternative to embedded SQL, for all operating systems. On OS/390, IMS DB uses DL/I APIs from COBOL, as another example.

Because you already know how to get at your data from COBOL, all of these topics are not covered here. The discussion in this chapter focuses on how to get your data from Java. To do that, the following options for data access from Java are discussed:

  • COBOL record-level access. A brief review is provided here, just to start the conversation and acclimate yourself with the database-access topic.

  • SQL. A brief introduction/review of SQL methodology, terminology, and statements is provided. This is necessary to read only if you are unfamiliar with SQL.

  • JDBC. This is the industry-standard Java-supplied data access method, and is patterned after Microsoft's ODBC (Open DataBase Connectivity) API. JDBC is a set of Java-supplied interfaces for which each database vendor supplies concrete implementations, in the form of JDBC drivers. Using the Java-supplied interfaces, you register a particular driver and submit dynamic SQL statements to that database.

  • SQLJ. This is a variant of JDBC that was defined by Oracle (with help from IBM and Tandem) and supported by both Oracle and IBM's DB2 on all systems. It allows you to use embedded SQL in your Java code. This is then precompiled into raw Java and a database plan. This can offer the performance benefits of static SQL over dynamic SQL.

This chapter focuses on these industry-standard mechanisms for accessing data from Java. However, as an OS/390 or OS/400 programmer, you have some other options that are unique, and supplied to you by IBM:

  • Record-level I/O. These are IBM extensions for accessing data one record at a time, as you are used to with COBOL's FILE CONTROL SECTION and OPEN, READ, WRITE, REWRITE, DELETE, and CLOSE statements.

  • JCICS. This is a way to write new CICS transactions in Java (mainly for OS/390).

  • CICS Transaction Gateway. This is a way to access existing CICS transactions from Java or any client talking TCP/IP. It uses EPI or ECI to communicate with CICS TS (for OS/390).

  • IMS TCP/IP OTMA. This is a client interface to IMS on OS/390, allowing you to access existing IMS transactions from any client talking TCP/IP. It uses XCF (Cross System Coupling Facility) to talk to the OTMA (Open Transaction Manager Access).

  • MQSeries client for Java. This is a Java client for accessing MQSeries message queues.

These options are covered in more detail in appendices D and E.

Well, there is a lot to talk about, but you still might not see your own favorite data-access method listed. If not, then you might have to content yourself with leaving that data access code in COBOL, and calling your COBOL program from Java. You might choose that option, anyway, if your goal is to wrap existing code in Java.

OS/390 programmers can refer to the IBM redbooks Integrating Java with Existing Data and Applications on OS/390, SG24-5142 and e-business Application Solutions on OS/390 Using Java: Volume 1, SG24-5342 for more information. (To find these references, search www.ibm.com/redbooks.) These redbooks describe numerous options for you in this area. In fact, all OS/390 programmers reading this book should read these redbooks next.

OS/400 programmers can refer to the (free) AS/400 Toolbox for Java product that contains Java classes for doing this. (Follow the links at www.ibm.com/as400/java.) The redbook Accessing the AS/400 with Java, SG24-2152-01, is a good reference to start with.

There is also support in IBM's VisualAge for Java Enterprise Edition product for connecting to all manner of existing data, applications, and transactions from Java. Yet another very good option for calling existing COBOL from Java is to use stored procedures. These are a nice way to call any program from any other program that supports SQL, as Java does.


COBOL Record-Level Database Access

COBOL programs often use the built-in COBOL database "record-level" access (that is, record input/output via statements like READ, WRITE, and DELETE) instead of embedded SQL. Database access then involves the following:

  • Assigning the file in the ENVIRONMENT DIVISION, using the SELECT statement. This assigns the COBOL name for the file to the actual file on disk (ASSIGN TO). It also indicates how the file is organized on disk (ORGANIZATION IS), how it will be accessed (ACCESS MODE), and for keyed access, the key field (RECORD KEY IS) or the alternative key field (ALTERNATE RECORD KEY IS), among other things.

  • Declaring the file in the FILE SECTION of the DATA DIVISION. This involves a File Description (FD) sentence that uses the COBOL name for the file, and clauses that describe the file and format of its records. For DB2/400 files, you can use a Format-2 COPY statement to copy in the externally described record format.

  • Opening the file using the OPEN statement, specifying the usage (INPUT, OUTPUT, I-O, or EXTEND) and the COBOL name for the file.

  • Positioning, if necessary, using the START statement to specify the COBOL name for the file, the comparison operation (e.g., KEY EQUALS), and the data value to compare to.

  • Accessing and updating the data, using the statements READ, WRITE, REWRITE, or DELETE to read, write, update, or delete the specified record or current record.

  • Closing the file by using the CLOSE statement, specifying the COBOL name for the file.

This COBOL built-in database support is sometimes called direct database access. It is noteworthy in the database industry because today most relational database access is done through some form of SQL statements, as discussed later in this chapter.

If you are conducting sensitive multiple-database transactions, which consist of multiple database updates each, you also use commitment control.

COBOL Commitment Control on OS/390

When working with VSAM files on OS/390, you do not have support for commitment control in COBOL. The COBOL statements COMMIT and ROLLBACK are not supported on OS/390 (but you'll notice they are reserved words).

If you are using the IMS DB, you can call SETU and SETS to set intermediate commit points, CHKP and SYNC to commit your changes, and the ROLL, ROLS, and ROLU APIs to rollback your changes.

CICS programmers can use the SYNCPOINT CICS command to commit changes and SYNCPOINT ROLLBACK to rollback changes.

COBOL Commitment Control on OS/400

Commitment control on OS/400 involves the following steps:

  • Prepare for commitment control using the CL commands CRTJRN (Create Journal), CRTJRNRCV (Create Journal Receiver) and STRJRNPF (Start Journal Physical File).

  • Start commitment control for your application using the CL command STRCMTCTL (Start Commitment Control).

  • Assign and declare the files in COBOL as usual.

  • Specify the COMMITMENT CONTROL FOR FileName clause of the I-O-Control paragraph in the FILE-CONTROL SECTION.

  • Open and work with the files as usual.

  • At appropriate times, commit the files' changes using the COMMIT statement, or, if any errors occur, cancel the files' changes using the ROLLBACK statement.

  • End commitment control using the CL command ENDCMTCTL (End Commitment Control).

Note

Commitment control can be scoped to either the job or an ILE activation group. Commitment control allows you to treat multiple database operations as a single atomic unit of work-either they all succeed, or none of them succeeds. This is the fundamental way in which complex transactions ensure database integrity; you do not want "partial" transactions to corrupt the state of your data.

If you are using embedded SQL, you can code embedded COMMIT and ROLLBACK SQL statements appropriately, rather than the COBOL COMMIT and ROLLBACK statements.


Accessing Data With SQL

SQL is an industry-standard method of accessing a relational database (containing rows and columns). The last major revision to the standard was ANSI SQL92, but by the time you read this, ANSI SQL99 (a superset of SQL92) will be the new standard. While many COBOL programmers still use direct record access to DB2/400 or VSAM, more and more programmers are looking at it as a portable way to access the database. Note that SQL is only valid for accessing DB2 on OS/390, not IMS or VSAM. However, SQL is a de facto standard in the industry for relational database vendors. Your SQL code and your SQL skills are transferable to other databases and other systems. As you will see, your SQL skills are also directly portable to Java.

SQL is a language that has a well-defined syntax for creating and manipulating databases. If you use it to create databases on OS/400, those databases are identical and interchangeable with database files created with DDS. When using SQL, you do not use the COBOL record-oriented style to access your data. Rather, you use statements where you specify record filter criteria to retrieve, update, or delete all rows (records) meeting that criteria.

In COBOL, you would code a loop to read through all records you want to view or update (hence record-level access), while in SQL, you first retrieve all rows using a filter. You then iterate through each row. Here is an example of a filter:

SELECT * FROM CUSTOMER WHERE STATE='PA'

In this case, all records from the CUSTOMER table are being retrieved, where the STATE field is equal to PA. Note that for each row, the filter specifies that all (*) the columns should be returned. Alternatively, you can specify an explicit list of comma-separated column names.

The WHERE clause in this statement is called the predicate. It can get as complex as required, using Boolean logic and numerous operators to explicitly identify the exact records you are looking for. For example, you can specify a COUNT function to count and report the number of rows, or an AVG function to compute and report the average value for all columns in all applicable rows. There is support for retrieving from multiple tables, for calculating totals, and more.

The SELECT statements have FROM and WHERE clauses, as shown in the example, for specifying the table and criteria for selecting rows from the table. A SELECT statement can also have GROUP BY, HAVING, and ORDER BY clauses. The GROUP BY and HAVING clauses allow you to apply column functions to subgroups of the rows you select, and restrict rows in the result after the functions have been applied. The following example is from DataBase Design and Programming for DB2/400 (but the SQL sections apply to OS/390 customers, too):

SELECT     ShpCity, COUNT( * ), AVG( Discount)
   FROM     Customer
   WHERE    Discount IS NOT NULL
   GROUP BY ShpCity
   HAVING   AVG ( Discount ) > .01

The ORDER BY clause can be used to sequence a result set, as in ORDER BY CUSTID.

As you explore SQL, you will be amazed at the power of the SELECT statement (see the DB2 Application Programming SQL manual for your system). The result of this query will be a temporary result set containing all of the returned rows. There are SQL statements for iterating through each row in the result set. (For example, in COBOL, FETCH moves you to the next row and places the column values for that row into specified data items.)

To update rows, there is no need for iteration. You simply specify the filter criteria for the affected records, and the new value for the column or columns you want to change. For example, consider this statement:

UPDATE CUSTOMER SET RATE=2 WHERE STATE='PA'

In this case, all rows in the CUSTOMER database table where the STATE column value is PA have their RATE value changed to the number 2. Is this easier than writing loops? You can also use a version of this that updates only the current row of the iterating loop.

Deleting rows involves a similar statement:

DELETE FROM CUSTOMER WHERE STATE='WY' AND RATE=2

This will delete all rows from the CUSTOMER table where the STATE value is equal to WY and the RATE column has a value of 2.

To insert rows into a table, you use the INSERT INTO statement, as follows:

INSERT INTO CUSTOMER (CUSTOMER, STATE, RATE) VALUES('Bobs Bait', 'MA', 1)

In this case, a single row is inserted into the CUSTOMER table with values specified for the columns CUSTOMER, STATE, and RATE. There is also syntax to insert multiple rows into the database in a single statement.

Interactive SQL Versus Embedded SQL

SQL consists of statements such as SELECT, UPDATE, DELETE, and INSERT. These statements can be run interactively, with the result shown immediately with the SPUFI facility on OS/390 and with the STRSQL (Start SQL) CL command on OS/400. These place you in a shell for submitting SQL statements. In this case, you are interactively executing a subset of the SQL statement language called dynamic SQL (discuss in the next section of this chapter).

Another option is to embed SQL statements directly into your COBOL, C, C++, PL/I, and FORTRAN source. Even REXX supports embedded SQL. In these cases, you use special syntax to distinguish between your native source and your SQL source. All SQL source is bracketed by EXEC SQL and END-EXEC. For COBOL, then, you might have the following:

123456*89012
            Exec SQL
               UPDATE CUSTOMER
                  SET   RATE=2
                  WHERE STATE='PA'
            End-Exec

All SQL statements are coded in positions 12 through 72, and most go in the PROCEDURE DIVISION. The SQL statements entered are free-format and case-insensitive. (However, the character literals you specify in statements are case-sensitive because they have to match the data in your database.)

One thing to note about embedded SQL is that you do not use a SELECT statement directly to retrieve a set of rows. Rather, you have to declare a cursor by using the DECLARE CURSOR statement, and specify SELECT there with the FOR statement clause:

Exec SQL
   DECLARE CustCursor CURSOR
      FOR SELECT CustID, CustName, Discount
         FROM Customer
         WHERE Discount IS NOT NULL
         ORDER BY CustID
End-Exec

Once declared, you OPEN the cursor, which builds an access path that reads the rows from the database that meet the SELECT criteria:

Exec SQL
   OPEN CustCursor
End-Exec

Your COBOL code then uses FETCH iteratively to get at each row from the result set, and processes the columns of that current row. The following would be coded in a loop:

Exec SQL
   FETCH NEXT
      FROM CustCursor
      INTO :cblCustID, :cblCustName, :cblDiscount
End-Exec

Note that cblCustID, cblCustName, and cblDiscount are COBOL data items. You can also update or delete the current row, which is known as a positioned update or delete, versus a searched update or delete:

Exec SQL
   UPDATE Customer
      SET  Discount = :cblNewDiscount
      WHERE CURRENT OF CustCursor
End-Exec

Finally your COBOL code uses CLOSE to close the cursor and free up the database memory:

Exec SQL
   CLOSE CustCursor
End-Exec

When you embed SQL source in your COBOL source, you no longer use the COBOL compiler command. Rather, the compilation is done using the appropriate SQL preprocessor command (such as the DSNHICOB JCL procedure on OS/390, or the CRTSQLCBLI command on OS/400). This preprocessor works in two steps, first processing the embedded SQL into native COBOL statements and an SQL access package, and then compiling the generated intermediate COBOL. On OS/390 when also using CICS, the CICS translation step must be done first.

For error-checking, you should always code the following in the WORKING-STORAGE of your COBOL program:

Exec SQL
Include SQLDA
End-Exec.

This gives you a group with numerous data items that are set automatically after SQL statements are executed. The two most important data items are SQLCode and SQLWarn0, which work very similar to the FILE-STATUS data item used for record-access programming. You should check them after each SQL statement. These are also what you check to stop the FETCH NEXT loop (look for SQLCode value 100). They will hold the values shown in Table 13.1.

Table 13.1: SQL Error and Warning Codes.
Condition | SQLCode | SQLWarn0
Error | <0 | Blank or W
No row found | 100 | Blank or W
Warning | 1-99 or 101 | Blank or W
Warning | 0 | W
Successful | 0 | Blank

Again, see the appropriate DB2 UDB Programming Guide for your system for all the details.

Static SQL versus Dynamic SQL

SQL statements can be either static or dynamic. SQL embedded in a programming language such as COBOL can be either of these. Static statements can only be embedded in another language and precompiled. Dynamic statements, on the other hand, can also be issued in many other ways (for example, interactively or via CLI APIs, as you will see). Dynamic statements must be prepared and executed dynamically (depending, say, on runtime information the program has), while static statements are hard-coded into the source program.

Although the statements themselves are hard-coded for static SQL, you can still use program variables to define the parameter values to the statement dynamically. To do this, you specify your program variable name right in the SQL statement, but prefix it with a colon. However, the Boolean logic for the filter is still hard-coded.

For example, while you can specify a variable for PA in the previous examples (e.g., WHERE STATE=:MYVAR), you cannot turn STATE= into a variable. For this, you can define multiple statements and use COBOL logic to determine which one to use at runtime, or just use embedded dynamic SQL. Some SQL statements are both static and dynamic, and some are only one or the other.

Static SQL typically offers better performance over dynamic SQL, because the precompiler has more information available for optimizations (such as caching the compiled version of the statements). That difference can often be minimized through the judicious use of performance-enhancing techniques, such as "preparing" often-repeated statements. In COBOL embedded SQL, use the embedded EXECUTE IMMEDIATE statement to execute a dynamic statement that is, perhaps, entirely defined in a variable. Your logic can determine at runtime exactly what the statement should be, and build it up on-the-fly into an alphanumeric data item, which you then submit via the EXECUTE IMMEDIATE statement. If a given statement will be executed more than once, you can save time by using the embedded command PREPARE first to prepare it in the database, and then use the EXECUTE statement repeatedly to run it.

Why use embedded dynamic SQL versus embedded static SQL? For flexibility. As we have discussed, dynamic statements allow you to decide at runtime which SQL statement to run. For example, if your program operates on a specific file, but allows the user or caller to decide what the query, update, delete, or insert action will be, you should choose dynamic over static.

Embedded SQL versus SQL CLI APIs

To use SQL in your programs, you have two choices:

  • Embed SQL statements (static or dynamic) inside your COBOL code, as discussed, and use the COBOL SQL preprocessor to translate those statements to native COBOL and invoke the COBOL compiler on that intermediate output.

  • Call SQL system APIs from your program. These APIs are based on the standard X/Open SQL CLI specification. They are similar to Microsoft's ODBC, which is also based on (and extends) the evolving X/Open standard. Basically, these APIs allow you to pass in SQL statements to be executed, although there are a few other APIs that perform functions besides just submitting SQL statements to the database manager-for example, there are APIs for connecting and disconnecting from a remote database.

Almost all COBOL programs that use SQL use embedded SQL, since it is both easier to use and more efficient. However, the CLI APIs do offer some additional advanced functions beyond embedded SQL. Furthermore, SQL preprocessors are typically supplied by each database vendor for its own database (the precompiler binds your programs to those of the database), while CLI APIs are far more database-vendor-independent (a more "standard" standard). A CLI program can potentially be written to easily target multiple vendor databases by dynamically connecting to the database, querying database support information, and passing in SQL statements on-the-fly.

CLI APIs are mainly used to execute passed-in SQL statements by submitting them to the database. When using CLI APIs, you can usually execute only dynamic SQL statements, but some static-only statements have equivalent CLI APIs. For example, the FETCH SQL statement is not dynamic, but there is an SQLFetch API in CLI.

Why use SQL CLI APIs versus embedded dynamic SQL? For even more flexibility. While dynamic statements allow you to decide at runtime which SQL statement to run, CLI APIs go further, and allow you to decide at runtime which table to operate against. These APIs provide the ultimate control at runtime, and offer the most functionality and flexibility. Furthermore, with CLI APIs, you can query information about the database itself-such as DB2/400 or DB2/390-and what SQL constructs it supports. This database-capability information is called metadata, a topic covered later in the discussion on accessing it from Java.

If your program allows the user to decide the table and the query or manipulation statements to run, you should choose CLI over dynamic SQL. Furthermore, if your program is designed to operate against multiple databases (such as DB2, Oracle, and Sybase) via end-user control, you will need to use CLI. This added flexibility is increasingly important in three-tier applications, Web-based applications, and truly portable applications that are less tied to particular database vendors.

Table 13.2 offers a summary of the different varieties of SQL, while Table 13.3 compares the attributes, benefits, and disadvantages of each.

Table 13.2: Static Versus Dynamic SQL.
SQL Statement Type | Run Interactively? | Embed in COBOL Source? | Invoke via CLI API?
Static | No | Yes | No
Dynamic | Yes | Yes | Yes
Table 13.3: Embedded Static Versus Embedded Dynamic Versus CLI APIs.
Embedded Static | Embedded Dynamic | CLI APIs
Best performance. | Need to use prepared statements to get performance. | Hardest to program.
Easiest to code. |  | Similar to embedded dynamic in performance.
Hard-coded table names and column names. | Hard-coded table names, but dynamic column names | Total flexibility in choice of table names and column names.

Local SQL versus Distributed SQL

SQL can be used by a program to access a database either locally (on the same system) or remotely (on a system other than where the program resides). The latter is called distributed SQL. Note that these are the same set of SQL statements we have been discussing, plus a few more that are unique for accessing remote data-such as the CONNECT statement for connecting to the remote database (something you must do before processing SQL statements for that database).

There are two types of distributed access, as dictated by IBM's Distributed Relational Database Architecture (DRDA or "Doctor DA"). The first, called Remote Unit Of Work (ROW), allows you to access a single database on a remote system. The second, called Distributed Unit of Work (DOW), allows you to access multiple databases on one or more systems, and synchronize transactions (as delimited by commit and rollback) across the multiple databases.

SQL Views and Indexes

Views are like traditional logical files or alternate indexes that allow you to define a filtered view of one or more files. You can then access that view from your program as though it were a physical file or table. Views provide significantly more filtering capability than alternative indexes on OS/390 or logical files on OS/400.

Indexes, unlike views, cannot be accessed from your program as though they were physical files or tables. Rather, they are tools that you use to define a permanent access path to your database file for performance reasons. Your code still refers to the original file, but the system recognizes when it is appropriate for an existing index to be used. This saves the overhead of creating a temporary access path on-the-fly. Indexes require some additional system overhead in terms of maintenance, however, so there is a tradeoff to be considered.

SQL Languages: DDL versus DML

So far, this chapter has discussed the various flavors of SQL statements, but not what SQL statements actually do. SQL statements are divided into two different sub-languages: DDL (Data Definition Language) and DML (Data Manipulation Language). DDL statements are used to create and alter entire tables. DML statements, in contrast, are for manipulating actual data-for example, reading, updating, inserting, and deleting rows. Table 13.4 shows the common DDL statements.

Table 13.4: Common DDL Statements.
Statement | Description
CREATE/DROP SCHEMA/COLLECTION | Creates or deletes a schema/collection.
CREATE/DROP TABLE | Creates or deletes a table.
CREATE/DROP INDEX | Creates or deletes an index.
CREATE/DROP VIEW | Creates or deletes a view.
ALTER TABLE | Adds, deletes, or changes a column, key, or constraint.
DROP PACKAGE | Deletes a package.
Table 13.5: Common DML Statements.
Statement | Description
DECLARE CURSOR | Declares a cursor, using the SELECT clause (embedded SQL only).
OPEN | Opens a cursor (embedded SQL only).
FETCH | Reads the next row of an opened cursor (embedded SQL only).
SELECT | Reads one or more rows (CLI only).
INSERT | Inserts one or more rows.
UPDATE | Updates one or more rows.
DELETE | Deletes one or more rows.
CLOSE | Closes a cursor (embedded SQL only).
COMMIT | Commits previous transactions (e.g. INSERT, UPDATE, DELETE), provides commitment control.
ROLLBACK | Undoes previous transactions, provides commitment control.


Why The Introduction To SQL?

As you learn about database support in Java, you will be using all these SQL concepts. SQL has undeniably become the industry standard for database access, and is supported by every major relational database vendor. If you are not using it already, we believe it is inevitable that you will be using it. When the Java engineers went searching for a standard and portable database access language, they didn't look very far! SQL was the answer, and knowing SQL is essential to accessing relational data from Java.

For OS/400 programmers, there is an alternative to SQL for accessing DB2/400 data from Java, which is covered in appendix E. However, if you use it, you are tied completely to OS/400 and to DB2/400. If that's what you want (we don't mind!), you might prefer to jump straight there instead of reading further here.


Accessing Data With Java

Java has built-in support for relational database access. Its support is patterned after Microsoft's ubiquitous ODBC standard. ODBC is a C-language set of APIs that abstracts out database access. It allows you to write database-accessing code once that easily targets multiple database vendors.

While SQL and CLI are standards, like many standards, they are open to interpretation and extension. All database vendors supply unique flavors of them. For example, the ANSI SQL standard is evolving quickly, and vendors are at various stages of implementing the new additions to this standard, such as database triggers and stored procedures.

ODBC is a successful attempt by Microsoft to design a standard set of APIs to which programs can code, independent of the database vendor. It is based on the CLI API standard, not embedded SQL. Why? Embedded SQL requires a precompiler to parse out and compile the SQL statements. These preprocessors are supplied by the database vendors, and each accepts the vendor's own flavor of SQL. To write vendor-neutral code with embedded SQL would be difficult.

Java's database support is patterned after ODBC, but is written entirely in Java (rather than C). It is, like ODBC, a framework for database access in which the following occurs:

  • Developers write their database vendor-neutral code using language-supplied syntax and support.

  • The database vendor supplies a piece to be "plugged in" to the infrastructure, allowing the database-neutral code to access that particular vendor's product.

Java's framework is written in Java and uses an object-oriented design, but the idea is the same. However, you'll see that Java's version is much easier to use. It really shows the beauty of objects, as they cleanly hide all the ugly details that ODBC has to expose through convoluted APIs. This Java framework is called, not surprisingly, JDBC (a trademarked name, but one that is often referred to as Java DataBase Connectivity). JDBC involves the following:

  • A Java package (java.sql) with classes and methods for database connectivity, manipulation, queries, and more.

  • A Java "core language" JDBC database driver manager, similar to ODBC's driver manager, that comes with the language.

  • Database vendor-supplied JDBC database drivers, which are unique to each database vendor. These "snap in" to allow a single JDBC application to access multiple databases with minimal code changes. The Java-supplied driver manager essentially passes on all SQL statements to the snapped-in database driver supplied by the vendor. While JDBC is database-neutral, you can still exploit unique database vendor functions, if desired.

  • A Java-supplied JDBC/ODBC bridge to allow, in the short term, Java access to any database via the database vendor's ODBC driver. You will only need and want to use this, however, if your database vendor is one of the few left on the planet that does not yet supply a Java JDBC driver. This JDK-supplied bridge will often be slower, and might only be as portable as the ODBC DLLs it requires.

The key is that you need to write your Java code to the Java.sql package, not the database vendor APIs. The classes and interfaces in this package pass on the requests to the underlying database on your behalf, as shown in Figure 13.1. This way, you can easily swap in the particular database with minimal impact to your code.

Figure 13.1: The JDBC frame-work.

Database vendors can supply any of four types of JDBC drivers:

  • Type 1: JDBC-ODBC bridge. This is supplied with the JDK, and can be used to connect to any ODBC driver.

  • Type 2: JDBC native driver. This is supplied by the database vendor, and uses Java Native Interface (JNI, which is the Java-supplied way of calling C functions from Java) to call existing C APIs for that particular database. It must be run on the same system as the database, but if the local database can access remote databases, then the Java JDBC wrapper will inherit this capability, too.

  • Type 3: JDBC net drivers. This is a pure Java implementation of the JDBC interfaces, for use in remote clients. It requires a piece of code running on the server (also supplied by the database vendor) to listen for the requests sent by client driver.

  • Type 4: JDBC thin drivers. This is also a pure Java implementation of the JDBC interfaces, for use in remote clients. The difference from type 3 is that there is no listener code required on the server because the client communicates directly with the database engine on the server.

JDBC Drivers for DB2/390

On OS/390, DB2 only supplies a single driver, of type 2. It is an efficient wrapper of the DB2 CLI APIs. This driver is available for all members of the DB2 family, on all operating systems. Note the OS/390 documentation refers to this as a "type 1" driver, but we prefer to think of it as type 2 because it only runs on OS/390 (versus running on top of any local or remote ODBC driver for OS/390 DB2). Also, it supplies its own Java JDBC driver, instead of relying on the JDBC/ODBC bridge supplied with the JDK. Further, it does not use the ODBC subprotocol in its URL syntax for connections, as you will see later in this chapter.

Because there is no type-3 or type-4 driver for OS/390 DB2 (as of this writing), there is no easy way to write client Java application or applet code that remotely accesses DB2 data. There are at least four ways around this:

  • Write your Java database access code to run on OS/390 and use Java Remote Method Invocation (RMI), CORBA Internet Inter Orb Protocol (IIOP), or IBM WebSphere Application Server Enterprise Edition to distribute your Java classes to the client. These are all methods of doing distributed objects, as discussed in chapter 14.

  • Write your Java database access code to run on OS/390, and use a communication mechanism of your own design to get the data to the client. For example, you might write your own TCP/IP sockets code to do this (reasonably easy in Java), or use Java Servlets and HTTP communications (also reasonably easy in Java). Java communication classes are supplied with the JDK in the java.net package.

  • Write your Java database access code to run on the client using the appropriate JDBC type-3 driver for the client DB2 family member (for example, DB2 UDB for Windows NT). Then use DB2 Connect to connect the client database to an OS/390 DB2 database. With this, your Java applet could run in a Web browser and invoke SQL statements via the all-Java JDBC driver to DB2 UDB for Windows, which would in turn shuttle the statements to DB2 for OS/390 via DB2 Connect. This is a three-tier architecture, as it requires a Windows system (or OS/2 or UNIX or Linux) between the applet client and OS/390 server.

  • Use a third-party type-3 or type-4 JDBC driver for DB2/390. For example, HiT Software has a highly regarded type-4 driver, available at www.hit.com.

These options are not covered here because the focus is on teaching you how to write JDBC code. Refer to the redbooks Integrating Java with Existing Data and Applications on OS/390 (SG24-5142) and e-business Application Solutions on OS/390 Using Java: Volume 1 (SG24-5342) for the explicit details on each option, including setup and pros and cons.

JDBC for DB2 OS/390 (FMID JDB5512) comes with DB2, but it requires the JDK on OS/390, of course. To get the JDK, you can order it (HJVA111 for releases 1 or 2, HJVA11A for subsequent releases), or download it from the Internet at www.ibm.com/s390/java/reg-ister.html. The JDBC driver requires you to add libdb2jdbc.so and libdb2jdbc_g.so (the debuggable version) to your LD_LIBRARY_PATH and your LIBPATH, and db2jdbcclasses.zip to your CLASSPATH on OS/390. See the redbooks above or the DB2 manual Application Programming Guide and Reference for Java (SC26-9018) for explicit setup details.

JDBC Drivers for DB2/400

On OS/400, there are two JDBC drivers available. The first is a type-2 driver that comes with the OS/400 JDK. It is a Java JDBC wrapper to the DB2/400 CLI APIs that are part of the operating system. The Java code you write using this driver can only run on OS/400 itself, but it is very efficient. Even though it must run on OS/400, it can be used to access databases on other AS/400s. Because this driver comes with the JDK, there is nothing unique you have to do to install it, other than install the JDK (which is on your stack tape as of V4R2 or higher). The JDK product number is 5769-JV1 for version 4 releases.

The second driver is a type-4 driver that comes with the AS/400 Toolbox for Java, which is a collection of pure Java classes that offer access to many OS/400 services, including data via this JDBC driver. All the Toolbox classes are designed to run either on the OS/400 itself or on any client that has a JVM. There is no prerequisite needed on any client (and a Web browser is a valid client), other than TCP/IP, which is built into most operating systems these days.

If you are writing client code to connect to OS/400 remotely, this second driver is the one for you. Because it is Java, it also runs fine on OS/400 itself, but because it is using TCP/IP to talk to the database engine, it is not as efficient as the type-2 driver that comes with the JDK. To use this driver, you must ensure that the Toolbox JT400.JAR file is in your CLASSPATH statement. See the installation instructions for the AS/400 Toolbox for Java for details. You can get the Toolbox and its documentation by going to www.ibm.com/as400/java and following the appropriate link. It also comes with your system as of V4R2 or higher, but it must be installed. The Toolbox product number is 5768-JC1 for version 4 releases.

Appendix E has more information about the other AS/400 Toolbox for Java classes. It also mentions an alternative direct-record-access option for database access that the Tool-box supplies.

Java JDBC Programming

The java.sql package that comprises JDBC is a Java equivalent to CLI. Recall from the earlier discussion about CLI APIs that they are a set of APIs for executing dynamic SQL statements. For JDBC, these are a set of classes for executing dynamic SQL statements.

The java.sql package consists of a number of public interfaces (recall that these are like abstract classes-only method signatures are supplied, not method implementations). Your code, however, uses them as though they were fully implemented classes. This is because each database-vendor-supplied JDBC driver simply implements each of these classes. The primary interfaces are shown in Table 13.6.

Table 13.6: The Main Interfaces in JDBC.
java.sql Interface | Description
Driver | The main interface, supplying the connection method for accessing a database.
Connection | Represents a specific session with a specific database.
Statement | Used to execute explicit SQL statements
PreparedStatement | Used to prepare and execute repeatedly executed SQL statements. Extends Statement.
ResultSet | Returned from an SQL query or stored procedure call, this contains the list of returned rows and methods for traversing them and extracting columns.
CallableStatement | Used to call stored procedures. Extends PreparedStatement.
ResultSetMetaData | Used to dynamically determine the column definitions for a ResultSet. Saves you from hard-coding the field attributes.
DatabaseMetaData | Used to dynamically determine information about the database itself. That is, it determines information about conventions and limits of the database (DB2, in our case), and information stored in the database, such as a list of registered, stored procedures.

Using JDBC involves, typically, the following steps:

  1. Import the java.sql package:

    import java.sql.*;

  2. Load the JDBC driver you want to use:

    DriverManager.registerDriver(new
com.ibm.as400.access.AS400JDBCDriver() );

    OR

    DriverManager.registerDriver(new
com.ibm.db2.jdbc.app.DB2Driver() );

    OR

    DriverManager.registerDriver(new ibm.sql.DB2Driver() );

  3. Connect to the database:

    Connection conn =
   DriverManager.getConnection("jdbc:as400://MYSYSTEM");

    OR

    Connection conn =
   DriverManager.getConnection("jdbc:db2:MYSYSTEM");

    OR

    Connection conn =
   DriverManager.getConnection("jdbc:db2os390:MYSYSTEM");

  4. Create the SQL-statement objects you want to execute:

    Statement stmt = conn.createStatement(); // OR
PreparedStatement pstmt =
conn.prepareStatement("... ? ... ?");

  5. Run the SQL statements:

    stmt.execute(String sql); // execute any SQL statement
stmt.executeQuery(String sql); // execute SQL SELECT
stmt.executeUpdate(String sql); // INSERT, UPDATE or DELETE

  6. Retrieve the information returned by the SQL statements:

    ResultSet rs = stmt.executeQuery(String sql);
while (rs.next ())
   String column1 = rs.getString(1); // get as a string
rs.close();

  7. Handle any errors resulting from the SQL statements-wrap every JDBC call in a try/catch block, for SQLException:

    try{...}
catch(SQLException exc)
{
System.out.println( "SQL error: " + exc.getMessage() );
}

  8. Close the statements when done:

    stmt.close(); // stmt is a Statement object from step 4
pstmt.close(); // PreparedStatement object from step 4

  9. Close the database connection when done:

    conn.close(); // Connection object from step 3

OK, ready to code? The following sections examine each of these steps in more detail.

Step 1: Import the java.sql Package

To use the classes and methods in the java.sql package, your source files must include the import java.sql.*; statement at the top of the .java file. To help you follow along, assume you are creating a new class to test your new JDBC skills, which is called TestJDBC1. So now, in the early days, it might simply look like Listing 13.1.

Listing 13.1: Starting a Java JDBC class by importing the java.sql package.
Start example
// file TestJDBC1.java
import java.sql.*;
public class TestJDBC1
{
   public static void main(String args[])
   {
      System.out.println("Inside TestJDBC");
   }
}
End example

Step 2: Load the JDBC Driver

Since you only import java.sql, not a specific package for a specific database driver, how do you tell Java which JDBC driver you want to use? The answer is with a static method called registerDriver in the DriverManager class that Java supplies. You give it an instance of the JDBC driver class you want to load.

Code this line for the DB2/400 type-4 driver that comes with AS/400 Toolbox for Java:

DriverManager.registerDriver(new
  com.ibm.as400.access.AS400JDBCDriver());

Code this line to use the DB2/400 type-2 driver that comes with the OS/400 JDK:

DriverManager.registerDriver(new
  com.ibm.db2.jdbc.app.DB2Driver());

Code this line to use the DB2-for-OS/390 type-2 driver:

DriverManager.registerDriver(new ibm.sql.DB2Driver());

This line of code needs only to be executed once per session, prior to any subsequent JDBC calls. Notice that this method throws the exception SQLException if it cannot find the class. So, the evolving test program might now look like Listing 13.2 (uncomment out the appropriate line to register the driver you want to use).

Listing 13.2: Registering the JDBC driver.
Start example
import java.sql.*;
public class TestJDBC2
{
   public static void main(String args[])
   {
      System.out.println("Inside TestJDBC");
      try
      {
         DriverManager.registerDriver(new
         com.ibm.as400.access.AS400JDBCDriver()); // DB2/400 Type 4
         // DriverManager.registerDriver(new
         // com.ibm.db2.jdbc.app.DB2Driver()); // DB2/400 Type 2
         // DriverManager.registerDriver(new ibm.sql.DB2Driver()); // DB2/390
      }
      catch(SQLException exc)
      {
         System.out.println("DB2 JDBC driver not found!");
         System.exit(1);
      }
      System.out.println("Class found OK");
      System.exit(0);
   }
}
End example
Note

Notice the use of System.exit(0) or System.exit(1) to end the program. Because the JDBC drivers spawn non-daemon threads in the background, you are forced to end your application with this, so that those background threads are killed.

Step 3: Connect to the Database

When using JDBC, you must connect to a database before using it. To make this connection, use the java.sql class DriverManager and its static method getConnection. This method returns an instance of a Connection class from the java.sql package.

In SQL terms, remember that the database you are interested in for this example is DB2. There is exactly one per OS/390 or OS/400 system. In step 2, you could have loaded multiple database drivers to allow simultaneous access to more than one database at a time. When you create a Connection object, you must uniquely identify the driver with which you wish to connect, and the actual database. This is done by specifying a URL that is unique for each JDBC driver. The driver manager gives this URL to each registered driver until one responds that it recognizes it. The URL starts with a unique identifier for the particular driver, followed by double slashes and the name of the system where the database exists.

In the case of this example, the URL for the DB2/400 type-4 driver (Toolbox) is specified as jdbc:as400://systemName. So, you might have the following:

Connection conn =
   DriverManager.getConnection("jdbc:as400://MYSYSTEM");

For the DB2/400 type-2 driver (JDK), the URL is specified as jdbc:db2://systemName or jdbc:db2:systemName (no slashes). For example, you might have this:

Connection conn =
   DriverManager.getConnection("jdbc:db2://MYSYSTEM");

For the DB2/390 type-2 driver, the URL is specified as jdbc:db2os390:systemName, as follows:

Connection conn =
   DriverManager.getConnection("jdbc:db2os390:MYSYSTEM");

The system name comes from the relational database directory. For OS/390, if it is not the local site, it must be specified in SYSIBM.LOCATIONS. Otherwise, it must be specified in field DB2 LOCATION NAME of the DISTRIBUTED DATA FACILITY panel during DB2 installation. On OS/400, for the type-2 driver, it must be specified in the WRKRDBDIRE command (the *LOCAL entry). For the AS/400 Toolbox for Java driver, the system name is the TCP/IP host name or the TCP/IP address of the system.

By the way, once you have the Connection object, you can call methods on it. One important method is setReadOnly(true), which you should call if you are only intending to read from the database. This allows for more efficient access by the database manager.

What might the sample program look like now? Let's create a new, non-static method to do the database connection, called testConnect. An instance variable for this Connection object also needs to be defined. Now that there is instance data and a non-static method, you will need to instantiate an instance of yourself as well. Then, you can call the new testConnect method on that instance (object), and test the connection. Pass in the system name to connect to as a parameter.

In Listing 13.3, we use the AS/400 Toolbox for Java JDBC driver, but you can uncomment the appropriate lines for one of the type-2 drivers. Note that if you run this from a client connecting to a remote DB2/400 database, you will be prompted for a user ID and password. Also, you need to specify your own system name, not leave it as "mySystem."

Listing 13.3: Connecting to the database.
Start example
import java.sql.*;
public class TestJDBC3
{
   Connection conn;
   static final String sysName = "mySystem";   // CHANGE THIS!

   public static void main(String args[])
   {
      System.out.println("Inside TestJDBC");
      try
      {
         DriverManager.registerDriver(new
         com.ibm.as400.access.AS400JDBCDriver ());
         // DriverManager.registerDriver(new
         // com.ibm.db2.jdbc.app.DB2Driver()); // DB2/400 Type 2
         // DriverManager.registerDriver(new ibm.sql.DB2Driver()); // DB2/390
      }
      catch(SQLException exc)
      {
         System.out.println("DB2 JDBC driver not found!");
         System.exit(1);
      }
      System.out.println("Class found OK");
      TestJDBC3 test = new TestJDBC3(); // create instance of us
      test.testConnect(sysName); // Attempt to connect to database
      System.exit(0);
   } // end main method
   public boolean testConnect(String sys)
   {
      System.out.println("Connecting to system " + sys + "...");
      try
      {
         conn = DriverManager.getConnection("jdbc:as400://"+sys);
         //conn = DriverManager.getConnection("jdbc:db2://"+sys); // DB2/400 Type2
         //conn = DriverManager.getConnection("jdbc:db2os390:"+sys); // DB2/390
      }
      catch (SQLException exc)
      {
         System.out.println("connect failed with: '" +
                           exc.getMessage() + "'");
         return false;
      }
      System.out.println("connected ok");
      return true;
   } // end testConnect method
} // end class TestJDBC3
End example
Note for AS/400 Toolbox Users:

If your connection still fails even after specifying a valid system name, ensure that your host server jobs are started by using the OS/400 command STRHOSTSVR *ALL (Start Host Servers). Also, ensure that your QUSER user ID is still valid, and that its password has not expired. The AS/400 Toolbox for Java JDBC driver uses this user ID, just as ODBC does.

More Details on the DriverManager.getConnection Method

There are two overloaded variants of the getConnection method. One of them takes an additional Properties object parameter (from the java.util package), and the other takes two additional String parameters for the user ID and password.

The latter getConnection overload is for making connections with hard-coded user and password values that are used to connect to the database. If you do not supply these, the type-2 drivers will use the existing job's user ID and password. The AS/400 Toolbox for Java driver will prompt for the user ID and password if you are running on a client, or in later releases, use the current job's user ID and password if you are running on OS/400.

The former getConnection overload is for specifying properties that affect the connection. You do this by instantiating a Properties object, and then using the put method to add name/value String pairs. The names are specific names the drivers have predefined, and the possible values of each are also predefined. These are listed in Table 13.7.

More Details on the DriverManager.getConnection Method for OS/400 Programmers

Furthermore, the actual URL syntax for the OS/400 drivers allows all of these properties to be specified right in the URL itself instead of by using a Properties object. The syntax is:

jdbc:as400(or db2)://systemName</defaultSchema<;list-Of-Properties>

The system name identifies which AS/400 you are going to connect to, and is required. The optional defaultSchema is the default OS/400 library to access in subsequent operations that do not specify a qualified file name. If no default is specified here, the library matching the name of the user ID at runtime is used, unless you change the naming property to system, in which case the default library list of your user ID is used to find the file. Yes, schema equals library in the JDBC terminology for OS/400. The optional list of properties is a semicolon-separated list of property=value specifications. A long list of properties is supported by the DB2/400 JDBC drivers. Table 13.7 lists them as of this writing; check the Toolbox documentation for a current list..

Table 13.7: JDBC Driver-Connection Property Values.
Property | Values | DB2 390 | DB2 400 | AS/400 ToolBox
user | User ID. | yes | yes | yes
password | User password. | yes | yes | yes
naming | sql or system: lib.file or library/file. Defaults to sql. | no | yes | yes
access | all (all SQL statements allowed), read call (SELECT and CALL allowed), and read only (only SELECT allowed). Defaults to all. | no | yes | yes
errors | basic (normal DB2 messages) or full (detailed DB2 messages). Defaults to basic. | no | no | yes
blocking enabled | For result set retrieval: true or false. Defaults to true. | no | yes | no
block criteria | 0 (no blocking), 1 (if FOR FETCH ONLY), 2 (unless FOR UPDATE. Default). | no | no | yes
block size | 0, 8, 16, 32, 64, 128, 256, or 512. Number of rows fetched per block. Default is 32. | no | yes | yes
trace | Turn on tracing for simple problem determination: true or false (the default). | no | yes | yes
transaction iso-lation | none (default), read committed, read uncommitted, repeatable read, or serializable. For commitment control. | no | yes | yes
do escape pro-cessing | true (default) or false. Can be faster if you turn it off. | no | yes | no
libraries | For JDBC Type 2, a single library name used as default library for unqualified statements. For Tool-box, multiple comma-separated libraries for unqualified, stored procedure calls and finding schemas in DatabaseMetaData. Default is *LIBL. | no | yes | yes
translate binary | true or false (default). True treats binary and varbinary data values as char and varchar. | no | yes | yes
prompt | If no user ID and password given, should a GUI prompt be tried: true (default) or false. | no | no | yes
date format | mdy, dmy, ymd, usa, iso, eur, jis. Server job default. | no | v4r5 | yes
date separator | / (slash), - (dash), . (period), , (comma), b (space). Job default. | no | v4r5 | yes
decimal separator | . (period), , (comma). Job default. | no | v4r5 | yes
time format | hms, usa, iso, eur, jis. Default determined by server job. | no | v4r5 | yes
time separator | : (colon), . (period), , (comma), b (space). Job default. | no | v4r5 | yes
prefetch | Prefetch rows for SELECT state-ments: true (default) or false. | no | no | yes
extended dynamic | Cache SQL statements in package on server: true or false (default). | no | no | yes
package | For extended dynamic, name of package. | no | no | yes
package library | For extended dynamic, library of package. | no | no | yes
package cache | For extended dynamic, cache packages in memory: true or false (default). | no | no | yes
package clear | For extended dynamic, clear package cache when full: true or false (default). | no | no | yes
package add | For extended dynamic, add packages to existing packages: true (default) or false | no | no | yes
package error | For extended dynamic, how to handle package related errors: except, warning (default), none. | no | no | yes
remarks | Where to get metadata remarks (SQL or system objects): sql or system (default). | no | no | yes
sort properties | Properties for how to sort the result sets, not listed here. | no | no | yes

For OS/400, the default naming convention used for a JDBC connection is naming=sql,. but you can also specify naming=system. This means that, in order to qualify a table reference on an SQL SELECT statement, for instance, you would specify COLLECTION/TABLE, as in MYLIB/MYFILE. It also means that an unqualified table name reference, as in TABLE, is equivalent to *LIBL/TABLE.

Step 4: Prepare the SQL Statements

To use JDBC, you must use SQL statements. The java.sql package includes two classes that you use to create instances to represent SQL statements. When you execute an SQL statement, the database performs two steps:

  1. Prepare the statement by effectively compiling it.

  2. Run the prepared statement.

The first step can be reserved for multiple-use statements, which saves time in the second and subsequent execution of the statement. Thus, the two flavors of SQL-statement classes in JDBC are the following:

  • Statement. Use this class to execute statements on-the-fly. If you have an SQL statement that is to be run only once, this is the class to use. Its executeXXX methods take any String object assumed to contain a valid dynamic SQL statement. If you expect to execute the same statement more than once, however, it is more efficient to use PreparedStatement.

  • PreparedStatement. Use this class to prepare a statement that will be executed multiple times. Note that it allows substitution variables, or markers, in the form of question marks (?), so that the same statement can be reused multiple times with different values. See the SQL documentation for exactly where you are allowed to use these markers, but essentially they can be used in SELECT statements for the value of a column name (as in WHERE STATE=?), in the SET clause of an UPDATE statement, and the VALUES clause in an INSERT statement. They cannot be used to substitute the table names or column names themselves. That would change the access path of the statement, so it cannot be turned into a variable. You must use separate statements for this. If you cannot hard-code these values, you have to use a Statement object instead of a PreparedStatement object.

To instantiate instances of these classes, you do not use the new operator. Instead, you use the createStatement or prepareStatement methods of your previously allocated Connection object to create these statements for you, as follows:

Statement         stmt  = conn.createStatement();
PreparedStatement pstmt =
   conn.prepareStatement("SELECT * FROM CUSTOMER WHERE CUSTNAME=?");

Notice that Statement objects do not specify the statement-to-execute at allocation time, whereas prepared statements do. This is an indication of the longevity of prepared statements. At the time prepared statements are created, as shown in the example, they are verified and "compiled" at the DB2 server. You must couch your prepareStatement method call in a try/catch block for SQLException, and pay attention to the exception-it usually implies that you have specified an improper SQL statement (or that you have not yet connected to the database, as in the previous step).

Having created an instance of Statement or PreparedStatement, you can subsequently execute that statement. Since executing statements, and then processing the results, are the interesting parts, we defer an example until we have covered these.

Step 5: Run the SQL Statements

Prior to running your prepared statements (PreparedStatement class objects), you have to specify the values for all the markers (question marks) you supplied in your statement. To do this, use the appropriate setXXX(int, value) method in the PreparedStatement class. The exact method to use depends on the type of the value you want to substitute, which, in turn, depends on the type of database field or column whose value you are supplying.

If you are specifying a value for a decimal field, you would use setBigDecimal. For character database field values, you would use setString. All of the setXXX variables take two parameters. The first is the relative one-based position of the marker to substitute. The second is the value to be substituted. These methods are the functional opposite of the getXXX methods you will see in the next step. For example, to set the substitution value for the previous section's example (SELECT * FROM CUSTOMER WHERE CUST NAME=?) you might specify the following:

pstmt.setString(1,"Phil Coulthard");

The number 1 for the first parameter indicates you are substituting for the first question mark. You must substitute for all the question marks before executing the prepared statement. These substitutions stay in effect for the life of the PreparedStatement object, or until reset or cleared (via the clearParameters method). The setXXX methods are shown in Table 13.8; for the convenience of OS/400 users, the DDS type equivalent is included.

Table 13.8: PreparedStatement Set Methods for Marker Substitution.
Method | SQL Type | DDS Type
setBigDecimal | NUMERIC | decimal
setBoolean | SMALLINT | binary(4,0)
setByte | SMALLINT | binary(4,0)
setBytes | VARBINARY | character, CCSID(65535)
setDate | DATE | date
setDouble | DOUBLE | float, FLTPCN(*DOUBLE)
setFloat | FLOAT | float, precision depending on value
setInt | INTEGER | binary(9,0)
setLong | INTEGER or BIGINT | binary(9,0)
setNull | NULL | sets the value to null for null-capable fields
setShort | SMALLINT | binary(4,0)
setString | CHAR, VARCHAR | character, VARLEN character
setTime | TIME | time
setTimestamp | TIMESTAMP | timestamp

To actually run or execute an SQL statement, you call one of three methods on the object:

  • The executeQuery method, if you are running an SQL SELECT statement. This method call sends the statement to the database and returns a ResultSet object. A result set is an instance of the ResultSet class, and there are methods for traversing it (covered in the next step):

    ResultSet rs = stmt.executeQuery(String sqlStmt): // for Statement
ResultSet rs = pstmt.executeQuery(); // for PreparedStatement

  • The executeUpdate method, if you are running an SQL INSERT, UPDATE, or DELETE statement, or SQL DDL statements like CREATE TABLE. This method call sends the statement to the database, where it is run. It then returns a count of the affected rows or zero for DDL statements:

    int result = stmt.executeUpdate(String sqlStmt): // for Statement
int result = pstmt.executeUpdate(); // for PreparedStatement

  • The execute method, if you are calling a stored procedure that returns multiple result sets. This is covered later in this chapter.

You will use the SELECT statement with the executeQuery method whether you are retrieving one row or multiple rows. The former is just a special case of the latter. For example, the following retrieves all the rows (and all the columns because of *) from file MYTABLE:

SELECT * FROM MYTABLE

whereas the following retrieves a single row from file MYTABLE-one where the unique key field has a value of A:

SELECT * FROM MYTABLE WHERE KEYFIELD = 'A'

This, then, is equivalent to COBOL's READ statement.

What SQL statements can you execute in your execute and executeUpdate methods? Basically, any dynamic SQL statement. You will primarily be using a SELECT statement via executeQuery to read data, and INSERT, UPDATE, and DELETE to manipulate data via

executeUpdate.

One popular question is, "Can I create tables, views, and indexes on-the-fly?" The answer, again, is yes. Use the appropriate SQL CREATE statement, which is submitted via the

executeUpdate statement.

Step 6: Retrieve the Information in Result Sets

After executing a SQL SELECT statement by using the executeQuery method, you will have an instantiated ResultSet object. This conceptually contains all the rows that met the criteria specified in the SELECT statement. You iterate sequentially through the result set by using the next method, which bumps the implicit cursor ahead by one row.

It is important to note that, initially, the cursor is positioned before the first row; you must do one next call to read the first row. The next call returns true as long as there is another row to advance to. When it returns false, you have reached the end of the rows (that is, the end of the file). For example, to iterate through a multiple-row SELECT statement's result set, you code the following:

ResultSet rs = stmt.executeQuery("SELECT * FROM MYTABLE");
while ( rs.next() )  // while more rows
   // do processing

For single-row SELECT statements, you are expecting exactly one returned row, so your logic might look like this:

ResultSet rs =
   stmt.executeQuery("SELECT * FROM MYTABLE WHERE KEYFIELD='A');
if ( !rs.next() ) // no rows?
  // issue error message
else
  // do processing

What is the "do processing" part of these examples? That is, how do you extract the values for each column of the current row? The trick is to know the following:

  • The relative position of the column (first column is position 1) or the explicit name of the column.

  • The data type of that column, and its equivalent Java data type.

With this information, you can declare a variable of the appropriate Java data type and populate it by using the appropriate getXXX method of the ResultSet class. There is one get method for each data type, and it returns the specified column's value of the current row as that data type. Note that all methods support two versions: one in which the first parameter is an integer representing the column position to retrieve, and the other in which the first parameter is a string representing the name of the column to retrieve. These are listed in Table 13.9 and are the functional opposite of the PreparedStatement class's setXXX methods, discussed in Table 13.8.

Table 13.9: get Methods for Retrieving Column Data from ResultSet.

getXXX method

SQL Data Types

Description

getBigDecimal(int,int) or (String, int)

DECIMAL, NUMERIC[1]

Second parameter is the scale-the number of digits after the decimal. Returns a java.math.BigDecimal object.

getBoolean

NUMERIC[1]

Returns a Java Boolean basic type: true for non-zero, false otherwise or if null.

getByte

SMALLINT[1]

Returns a Java byte basic type (one byte, signed).

getBytes

BINARY, VARBINARY

Returns a Java byte array basic type: byte[ ].

getBinaryStream

BINARY, VARBINARY

Returns a java.io.InputStream object.

getAsciiStream

CHAR, VARCHAR, BINARY, VARBINARY

Returns a java.io.InputStream object.

getUnicodeStream

CHAR, VARCHAR, BINARY, VARBINARY

Returns a java.io.InputStream object.

getDate

CHAR, VARCHAR, DATE, TIMESTAMP

Returns a java.sql.Date[2]object.

getDouble

FLOAT, DOUBLE[1]

Returns a Java double basic type.

getFloat

REAL[1]

Returns a Java float basic type.

getInt

INTEGER[1]

Returns a Java int (integer) basic type.

getLong

INTEGER[1]

Returns a Java long basic type.

getShort

SMALLINT[1]

Returns a Java short basic type.

getString

any

Returns a java.lang.String object.

getTime

CHAR, VARCHAR, TIME, TIMESTAMP

Returns a java.sql.Time[2]object.

getTimeStamp

CHAR, VARCHAR, DATE, TIMESTAMP

Returns a java.sql.Timestamp[2]object

[1]The recommended type(s) are shown above, but this method can actually be used for SQL types SMALLINT, INTEGER, REAL, FLOAT, DOUBLE, DECIMAL, NUMERIC, CHAR, and VARCHAR (and TINYINT, BIGINT, BIT, and LONGVARCHAR). For more information on data types, see the section "More on Data Types."

[2]The Date, Time, and Timestamp classes in java.sql all extend the java.util.Date class described in chapter 8. The Date and Time classes only write the date or the time portion when writing data via JDBC, versus both values which java.util.Date holds. Also, Time and Timestamp add nanoseconds, as required by SQL standards.

As previously mentioned, you can always specify the name or the one-based position of the column. It is recommended that if your SELECT statement uniquely identifies the columns (as in SELECT COL1, COL2 FROM TABLE), use the name again on the getXXX method call. If you specify that you want all columns (as in SELECT * FROM TABLE), use the column numbers because the names are probably not known. If you know the column name but want to specify the column number, you can use the ResultSet method findColumn(String). It is always more efficient to use this once outside the result-set processing loop, and then use the column number versions of the get methods inside the loop. The numeric version is faster than the name version of these methods.

Notice that there are three getXXXStream methods, Binary, ASCII, and Unicode. (The getUnicodeStream method is deprecated in JDBC 2.0 in favor of the new getCharacter Stream.) These methods are intended for very large fields, such as bitmap images. They are alternative methods that allow you to retrieve these values in smaller, fixed-size blocks, rather than as a single "blob" with the getBytes or getString methods. To use them, you must access the returned java.io.InputStream objects immediately. Otherwise, you face losing them on the next rs.getXXX or rs.next call. The following example is from the JDBC Guide: Getting Started document included in the JDK documentation:

java.sql.Statement stmt = con.createStatement();
ResultSet r = stmt.executeQuery("SELECT x FROM Table2");
// Now retrieve the column 1 results in 4 K chunks:
byte buff = new byte[4096];
while (r.next()) {
   Java.io.InputStream fin = r.getAsciiStream(1);
   for (;;) {
      int size = fin.read(buff);
      if (size == -1) // at end of stream
         break;
      // Send the newly filled buffer to some ASCII output stream:
      output.write(buff, 0, size);
   }
}

Recall that when using SQL in COBOL programs, your processing for SELECT involves using DECLARE CURSOR and specifying the select statement as a clause of this declaration (using the FOR clause). Thus, in COBOL, you name your cursor, whereas in JDBC it is implicit. The ResultSet class and its next and getXXX methods are equivalent to embedded SQL's FETCH NEXT and FETCH NEXT INTO statements.

There might be times when using JDBC when you want to process an UPDATE or DELETE on the current row, instead of specifying a row selection criteria. This is called positioned update and delete in SQL terms. To do this, you need some way in your subsequent UPDATE or DELETE statement to specify the result set, and the current row-position within it. Keep in mind that the result sets are actually implemented and stored in the database. They are given names by the database, even when you do not do so in JDBC. To get this name so you can specify it in your UPDATE or DELETE statement, use the ResultSet method getCursorName. Here is an example:

while ( !rs.next() )
{
   ...
   String sql = "DELETE FROM MYTABLE WHERE CURRENT OF ";
   sql = sql + rs.getCursorName();
   stmt.executeUpdate(sql);
   ...
}

There is also a setCursorName method if you choose to explicitly name the result set. This method is part of the Statement class, not the ResultSet class.

Note

When you do a positioned update or delete as shown here, you must use a different statement object than the one used to produce the result set.

Reading Rows a Few at a Time

You might be wondering whether you can read the database n rows at a time. Yes, you simply code your result-set processing to process only the next n records at a time. For example, you might have a method called readNextNRows:

public short readNextNRows(ResultSet rs, short n)
   throws SQLException
{
   short readRows =  0; // return how many rows actually read
   for (boolean more = rs.next();
      more && readRows < n;
      readRows++, more = rs.next())
   {
      // process the current row
   }
   return readRows;
}

Of course, this requires some coding on your part. It is not absolutely necessary to bother with this, though. Instead, you can take a shortcut by placing the result-set processing in a separate thread, so that it runs in the background and can be interrupted by the user. In most cases, this effectively removes the need to only retrieve n records at a time because the user is not forced to wait for all records to be read before continuing.

The information on threads in chapter 11 should make this easy for you to code. Simply place the database-retrieval code in a class that implements Runnable, and have the run method do your database retrieval. Also, include a private stopped Boolean variable, and a stopReading method that sets it to true. Then check if stopped == true in your record processing loop. Code that uses this class to retrieve records in the background can stop that retrieval by simply calling the stopReading method, as shown in Listing 13.4.

Listing 13.4: How to process result sets in a thread.
Start example
import java.sql.*;
public class ReadRowsInBackground implements Runnable
{
   private ResultSet rs;
   private boolean   stopped = false;

   public ReadRowsInBackground(ResultSet rs)
   {
      this.rs = rs;
   }
   public void stopReading()
   {
      stopped = true;
   }
   public void run()
   {
      try
      {
         while (!stopped && rs.next())
         {
            // process the current row
         }
         rs.close();
      }
      catch (SQLException exc)
      {
         // handle exception
      }
   } // end run method
} // end class ReadRowsInBackground
End example

To use this class, then, you would code the following:

ReadRowsInBackground readThread = new ReadRowsInBackground(rs);
readThread.start();

Of course, if you are populating a list box, you will have to change the constructor to also accept a list box object so your run method can put the retrieved rows directly into it. You should also include a "Stop" push button in your user interface that, when pressed, stops the database retrieval with readThread.stopReading();. You can also call this when the user has chosen the record with which he or she wishes to work.

If you do decide to process your queries in a thread (and we suggest you do), two meth- ods in the Statement, PreparedStatement, and CallableStatement classes (for stored procedures; covered shortly) will be of use in this scenario:

  • The cancel() method can be called by another thread to cancel a long-running query prior to executeQuery returning.

  • The setMaxTimeout(int seconds) method can set an upper limit in seconds indicating how long to wait for the SQL query to end. The default is zero, no time limit.

Another potentially useful method is setMaxRows(int max) to set an upper limit on how many records to be retrieved in a result set. Again, the default is zero, no limit.

Note

You might still decide to retrieve only a page or two of records at a time if memory is a concern. That is, if your list involves, say, tens of thousands of records or more, it would clearly be reasonable to load only on demand. This could be easily coded into the running example by specifying, in the constructor, the number of records to retrieve each time, and changing the loop to only retrieve that many "next" records.

One last note on result sets. They do not take up memory in your Java program equivalent to the number of rows selected by the SELECT statement. Only database memory is taken. Your Java program contains memory at any given time only for the current row. This will not be true if you specify the blocking property on the connection (and it applies), in which case there will be Java memory allocated for the block of rows retrieved, behind the scenes.

Step 7: Handle any Errors

You have seen error-handling already: wrap any of your JDBC methods in try/catch blocks for SQLException. Alternatively, include throws SQLException in your method definition, and let the caller of the method handle it.

You should be aware, though, that there are cases where JDBC (actually SQL) will issue warnings, not errors. This might be due to data truncation, implicit commits, and so on. To be thorough, or for SQL statements that you know could result in SQL warnings, you can check for any warnings after executing an SQL statement (view execute,

executeQuery, or executeUpdate) by using the method getWarnings in the Statement

class. This returns the first warning in the form of an instance of the SQLWarning exception class. This object, then, supports the getNextWarning method for retrieving additional warning messages if there is more than one. Keep in mind that you should always call your Statement object's clearWarnings method before executing a statement to be sure that any subsequent warnings are actually issued for that statement execution:

stmt.clearWarnings(); // stmt == Statement object
stmt.execute(sqlString); // execute sqlString SQL statement
SQLWarning warning = stmt.getWarnings();
while (warning != null)
{
   warning = warning.getNextWarning();
   System.out.println("Warning: " + warning.getMessage());
}

Finally, rather than checking the warnings at the statement-object level, you can check them at the Connection level itself, to retrieve all the warnings posted for a number of statement executions. Note that the method names and usage are identical:

conn.clearWarnings(); // conn == Connection object
// execute one or more statements
SQLWarning warning = conn.getWarnings();
while (warning != null)
   warning = warning.getNextWarning();

If this is not enough flexibility, you can even check them on the ResultSet object, again using the identical methods.

Truncation warnings actually have their own Java exception class, DataTruncation, which inherits from the SQLWarning class. SQLWarning also inherits from SQLException, so you get access to all the methods in all these classes for DataTruncation exceptions. The DataTruncation class includes methods such as getDataSize to return how many bytes should have been processed, and getTransferSize to return how much data actually was processed (on a read or a write).

Another useful method is getIndex, which returns the column number of the truncated field. If you suspect a truncation warning, you can use the inherited getSQLState method of your SQLWarning object to check for the truncation state, 01004, and then cast your object to a DataTruncation object in order to access the unique methods in that class:

if (warning.getSQLState().equals("01004"))
  {
    DataTruncation t = (DataTruncation)warning;
    int colIdx = t.getIndex();
    int truncd = t.getTransferSize();
    System.out.println("Column " + colIdx +
      " truncated at " + truncd + "bytes");
}

Normally, you will never get this exception. However, it can happen if you use the setMaxFieldSize(int) method on a Statement or PreparedStatement object. This method places an upper limit on how many bytes will be read or written to the database for any column.

Step 8: Close the Statements

If you do not close your Statement and PreparedStatement objects, it will be done for you when these objects are garbage-collected. Still, because they typically involve some database resources as well, it is very important to explicitly close them via their close method as soon as you have established that you are finished with them. The same practice applies to ResultSet objects and, as you'll see later, CallableStatement objects.

Note

Any currently open ResultSet for a statement is closed implicitly when you execute another statement with the same Statement object.

Step 9: Close the Database Connection

Just as with Statement objects, it is a good practice to close your database connection (the Connection object) when you have determined programmatically that you are finished with it. Due to the expense of establishing a connection, some programmers prefer to keep the connection alive so it can be used on subsequent requests. We stress again, though, that you need to close all statements and connections as soon as you can to keep database resources from being consumed.

A Database Query Example

Let's evolve the running example to include a database query, in Listing 13.5. The sample is modified to include a new instance variable for the Statement object, and a new testQueryAll method. All the records are read from the OS/400 PDM user-defined-options database in QGPL. (We choose this database because it is both small and likely to exist on your system.) OS/390 programmers can substitute a database of their choosing. Our database has only two columns, which are both character type. For example, you might use SELECT * FROM SYSIBM.SYSTABLES, which gives you a single column of type character.

Listing 13.5: The database query example.
Start example
import java.sql.*;
public class TestJDBC4
{

   Connection conn;
   static final String sysName = "mySystem";   // CHANGE THIS!
   PreparedStatement pstmt;

   public static void main(String args[])
   {
      ... same ...
      TestJDBC4 test = new TestJDBC4(); // create instance of us
      if ( test.testConnect(sysName) )
         test.testQueryAll();
      System.exit(0);
   } // end main method
   public boolean testConnect(String sys)
   {
      ... same ...
   }
   public boolean testQueryAll()
   {
      System.out.println("querying all...");
      try
      {
         if (pstmt == null) // only create once
            pstmt = conn.prepareStatement("SELECT * FROM QGPL.QAUOOPT");
         ResultSet rs = pstmt.executeQuery();
         System.out.println("query results:");
         while (rs.next())
            System.out.println(rs.getString(1) + " " +
                              rs.getString(2).trim());
         rs.close();
         pstmt.close();
      } // end try
      catch (SQLException exc)
      {
         System.out.println("query all failed with: '" +
                              exc.getMessage() + "'");
         return false;
      }
      System.out.println("query done");
      return true;
   } // end testQueryAll method
} // end class TestJDBC4
End example

Here are the results of running the program:

Inside TestJDBC
Class found OK
Connecting to system mySystem...
connected ok
querying all...
query results:
 C CALL &O/&N
CC CHGCURLIB CURLIB(&L)
CD STRDFU OPTION(2)
...
TD STRSDA OPTION(3) TSTFILE(&L/&N)
WS WRKSBMJOB
query done

At this point, you have seen the introductory tour to Java's JDBC (and its SQL base). The next sections dig deeper into the details of data types, database constructs, metadata, and more. If you are merely looking for an introduction, you can safely skip directly to the next chapter. If you are ready for more details, read on.

More on Data Types

When using SQL, whether it is embedded in COBOL code through CLI APIs or through JDBC, you use predefined SQL data types. This ensures consistency and portability from one database vendor to another. When using SQL in COBOL, these data types are mapped to and from COBOL-native data-type variables by the database.

The operation is similar for JDBC. You have seen how the getXXX methods in the ResultSet class allow you to equate an SQL column's data type to a Java basic data type or a Java object data type (like java.math.BigDecimal). You have also seen how the setXXX methods in the PreparedStatement class allow you to equate a Java basic data type or Java object.

The conversion to and from EBCDIC values and Java's ASCII values happens automatically for you, including codepage conversions. Table 13.10 is a summary of the SQL data types and their Javal equivalents. (This data-type map- ping is merely the preferred mapping for performance reasons, but you can easily map to and from various types-as you saw, for example, in the setXXX methods.)

Table 13.10: SQL versus Java Data Types.
SQL Type | Java Type
BIGINT | int or long
BINARY | byte[]
BIT | short
CHAR | String
DATE | java.sql.Date
DECIMAL | java.math.BigDecima
DOUBLE | double
FLOAT (n) n=1-14 | float
FLOAT (n) n-25-53 | double
INTEGER | int
LONGVARBINARY | byte[]
LONGVARCHAR | String
NUMERIC | java.math.BigDecimal
REAL | float
SMALLINT | short
TIME | java.sql.Time
TIMESTAMP | java.sql.Timestamp
TINYINT | short
VARBINARY | byte[]
VARCHAR | String

The SQL data types are specified on the SQL statements CREATE TABLE and ALTER TABLE when specifying the data types for the new columns. The matching java.sql.Types integer constants are used in JDBC on the getObject methods in the ResultSet class, and on the setObject method of the PreparedStatement class. These methods allow you to set and read database values with Java objects, rather than specific Java type variables. These are also used on the CallableStatement class's registerOutParameter methods, and returned from the metadata classes when dynamically querying field type information, as you'll see soon.

Notice that some SQL data types map to non-primitive Java data types-that is, to Java classes. For example, the DATE, TIME, and TIMESTAMP SQL data types map to the classes Date, Time, and Timestamp in the java.sql package. Furthermore (and probably closest to your heart), the DECIMAL SQL data type is mapped to the BigDecimal class in the java.math package. These classes were created to handle the data types that SQL databases support, but that Java, prior to JDBC, did not. You need to look at the Java documentation for these classes to see how to create, query, test, manipulate, and convert objects of these classes. There are methods for all of these.

Null-Value Considerations in JDBC

As you probably know, the SQL standard allows "null capable" columns, which you can explicit set to null to identify an unset value. This is in place of the old method, where you used your own hard-coded special values to indicate unset columns. Of course, this implies you need a way for your program to indicate a column should be set to null on a write, or query if it is null on a read.

In Java's JDBC, you can call the ResultSet method wasNull for the previously read column of the current row via the getXXX method. It will return true if that column value is null.

To write a null value to a field, use PreparedStatements and the setNull method. This method takes as the first parameter the integer position of the marker, and the SQL type of the column that marker represents (a constant from the java.sql.Types class), for example, stmt.setNull(1,Types.INTEGER);. Further, any of the setXXX methods that take an object accept a null parameter for that object, and if given null, will write an SQL null to the database for that column in that row.

Commitment Control in JDBC

JDBC supports commitment control, of course, because it is a major part of the SQL standard. To use commitment control with JDBC, you first must specify a transaction isolation level. By default, this is "none," but you can specify it via a call to the connection object's setTransactionIsolation method. For OS/400 programmers, the property values correspond to the COMMIT parameter on the CRTSQLXXX preprocessor commands, while the setTransactionIsolation parameter is a constant integer defined in the .sql.Connection class.

The transaction isolation affects how database objects, like selected rows, are locked for the life of a transaction relative to other concurrent transactions. A transaction is one or more SQL statements. The transaction isolation options are listed in Table 13.11. See the DB2 SQL reference documentation for information on these isolation levels.

Table 13.11: Transaction Isolation Constants from the java.sql.Connection Class.
Transaction Isolation Property | setTransactionIsolation method | OS/400 CRTSQLxxx COMMIT parameter
"none" | TRANSACTION_NONE | *NONE or *NC
"read committed" | TRANSACTION_READ_CO MMITTED | *CS
"read uncommitted" | TRANSACTION_READ_UN COMMITTED | *CHG or *UR
"repeatable read" | TRANSACTION_REPEATAB LE_READ | *RS
"serializable" | TRANSACTION_SERIALIZA BLE | *RR

Once you choose and specify a transaction isolation level, you must next decide whether to do explicit commit/rollback calls, or use the default setting of autoCommit. In this default mode, all SQL statements are automatically committed after their last result-set row has been read (for executeQuery) or the statement has been executed (for executeUpdate). An SQL statement is also implicitly committed when you execute another statement, if it has not been already. You can, and probably will, turn off autoCommit mode by calling setAutoCommit(false) on your connection object. In this case, you explicitly code your own calls to the commit or rollback methods on your connection object. (See the documentation on the Connection class for more information.)

Retrieving Database Metadata in JDBC

JDBC, and SQL in general, provide a way to query schema information at runtime, such as the column names, types, and lengths, so you do not have to hard-code it. This information, known as metadata, includes everything you need to know about a column definition. Of course, using this dynamic approach results in a performance hit, but if you are really concerned about maintenance and integrity, you might decide that it is worth the sacrifice. Actually, if you are writing a truly dynamic application where you cannot predict the file or fields that will be used, you must use this technique.

You use the ResultSet class's getMetaData method to return information about the columns for this result set. This information includes the number of columns, and, for each column, the name, label, type (as an integer mapping to one of the java.sql.Types values), type name (as a string), precision (total length), scale (number of decimals), and even something called the display size. These are all retrieved using the appropriate getXXX method in the ResultSetMetaData class.

Listings 13.6 and 13.7 comprise a Java application designed to test the retrieval and display of metadata information for the file QCUSDATA in library QPDA, which all OS/400 development boxes have (again, OS/390 programmers should substitute in their favorite table). This example builds on the previous one. It simply defines a new method. While not shown here, the code that called testQueryAll is changed to call testMetaData (the new method), instead. Then, a couple of small helper methods are needed to pad strings out to a given width, followed by the actual testMetaData method.

Listing 13.6: Helper methods for padding strings.
Start example
public static final String padString(String padString, int finalLen)
{
   StringBuffer temp = new StringBuffer(padString);
   int curLen = padString.length();
   int padAmount = finalLen-curLen;
   for (int idx = 0; idx < padAmount; idx++)
      temp.append(' '); // append blank
   return temp.toString();
}
public static final String padString(int padInt, int finalLen)
{
   return padString(Integer.toString(padInt),finalLen);
}
End example

Listing 13.7 is the actual testMetaData method that uses the above helper methods. (The full sample for this application is in the TestMetaData.java file on the CD-ROM. Note the uppercase T in the file's name.)

Listing 13.7: TestMetaData constructor to display metadata information.
Start example
public boolean testMetaData()
{
   System.out.println("querying metadata...");
   try
   {
      Statement stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery("SELECT * FROM QPDA.QCUSDATA");
      ResultSetMetaData rsmd = rs.getMetaData();
      int nbrColumns = rsmd.getColumnCount();
      int colIdx;
      System.out.println("Column Information");
      System.out.println("==================");
      System.out.println("Name       " + "Label      " + "TypeName   " +
         "Digits " + "Decs " + "DispSize ");
      System.out.println("------" + "------" + "------" + "----" +
         "---" + "-----");
      for (colIdx=1; colIdx<nbrColumns; colIdx++) // loop through columns
      {
         System.out.print(padString(rsmd.getColumnName(colIdx),11));
         System.out.print(padString(rsmd.getColumnLabel(colIdx),11));
         System.out.print(padString(
                  rsmd.getColumnTypeName(colIdx),11));
         System.out.print(padString(rsmd.getPrecision(colIdx),8));
         System.out.print(padString(rsmd.getScale(colIdx),5));
         System.out.println(padString(
                  rsmd.getColumnDisplaySize(colIdx),9));
      } // end column for-loop
      rs.close();
      tmt.close();
   } // end try
   catch (SQLException exc)
   {
      System.out.println("Failed with: '" + exc.getMessage() + "'");
      return false;
   }
   return true;
} // end testMetaData method
End example

The result of running this sample follows:

querying metadata...
Column Information
==================
Name      Label     TypeName   Digits  Decs   DispSize
CUST      CUST      CHAR       5       0      5
NAME      NAME      CHAR       20      0      20
ADDRESS   ADDRESS   CHAR       20      0      20
CITY      CITY      CHAR       20      0      20
STATE     STATE     CHAR       2       0      2
ZIP       ZIP       DECIMAL    5       0      7
SEARCH    SEARCH    CHAR       6       0      6
CUTYPE    CUTYPE    CHAR       1       0      1
ARBAL     ARBAL     DECIMAL    8       2      10
ORDBAL    ORDBAL    DECIMAL    8       2      10
LSTAMT    LSTAMT    DECIMAL    8       2      10
LSTDAT    LSTDAT    DECIMAL    6       0      8
CRDLMT    CRDLMT    DECIMAL    8       2      10
SLSYR     SLSYR     DECIMAL    10      2      12

Other methods are available that you might find useful. Some of these are shown in Table 13.12.

Table 13.12: ResultSetMetaData Methods.
Method | Description
isAutoIncrement | Always false.
isCaseSensitive | Always true for text types such as CHAR, and always false for numeric types like DECIMAL.
isCurrency | Always false.
isDefinitelyWritable | Always true.
isWritable | Always true.
isNullable | True if null-capable.
isReadOnly | Always false.
getColumnCount | The number of columns.
getColumnDisplaySize | The display size needed to show this column's value. For example, for decimal columns, it is the total length plus two for the decimal point and a sign.
getColumnName | The column name.
getColumnType | The column data type. One of the java.sql.Types integer constants
getColumnTypeName | Same as above, but as a readable string.
getPrecision | The total length (including decimals).
getScale | Decimal positions.

The idea behind metadata is that you can query this information rather than hard-coding it. That allows your Java code to automatically handle any changes in your database definition.

There is also a class called java.sql.DatabaseMetaData, which offers a vast amount of in- formation about the database itself (that is, DB2). It can provide even more dynamic information if it is needed for truly user-driven applications. An instance of this is returned using the Connection object's getMetaData method. The information returned in the large number of methods in this class can be categorized as shown in Table 13.13.

Table 13.13: DatabaseMetaData Functionality.
Category | Example | Description
Database support information | supportsFullOuterJoins(), getDateTimeFunctions() | Describes what SQL functionality this database (DB2) supports.
Database terminology information | getCatalogTerm(), getProcedureTerm() | Lists what terminology this data- base uses for common items.
Database name and version information | getDatabaseProductVersion(), getDriverVersion() | Indicates what version this data-base or JDBC driver is at.
Cataloged information | getProcedures(), getTables(), getSchemas() | Gets lists of database-related ob-jects from the system.
Specific information | getColumns(), getProcedureColumns(), getCrossReference(), getExported/ImportedKeys(), getIndexInfo() | Gets explicit information about a given database object.

The last two categories are the two that you might need to use. The other categories are only needed for completely portable, database-neutral, dynamic applications, such as a generic database query tool.

The one method you might really need to use is getColumns, if you want to retrieve the label or field description for a column. This method returns a ResultSet class object containing the metadata for every column (by default) in the specified table of the specified schema. For every column in the file, you get one row in the result set. It contains 17 pieces of information, one piece in each column of the result set, much of it useful: (1) catalog name, (2) schema name, (3) table name, (4) column name, (5) data type, (6) type name, (7) column size, (8) buffer length, (9) decimal digits, (10) numeric precision radix, (11) nullable, (12) remarks, (13) column definition, (14) SQL data type, (15) SQL date/time substitution, (16) character octet length, and (17) ordinal position.

Phew! As you can see, you are interested in column 12, the "Remarks" column. The following will give you the label:

DatabaseMetaData dbmd = conn.getMetaData();
rs = dbmd.getColumns(null, "QPDA", "QCUSDATA", null);
while (rs.next())
   String label = rs.getString(12);
Note for OS/400 Programmers:

The label is the TEXT value, not the COLHDG value. There is no metadata method for retrieving this because COLHDG is a unique OS/400 value.

Good Stuff: Stored Procedures, Triggers, and RI

As part of the evolving SQL standard, new database constructs have been defined, including the following:

  • Constraints. These are rules about key validation, often called Referential Integrity, or RI.

  • Triggers. These are user-supplied programs called when records are added, changed, or deleted.

  • Stored procedures. These are user-supplied programs called directly via SQL statements.

All of these constructs are part of an effort to allow you to move your database rules out of each of your programs and into the database itself. This is a very important concept. It means that you can increase the number of ways in which your users can access your data, without compromising the data integrity.

Currently, much of the input validation for a database is done by your COBOL program logic. This logic is often duplicated across multiple programs and is subject to multiple maintenance problems. Also, to allow access to the data from, say, a Java client or even a Java server program, you have to duplicate that logic or code your Java programs to call your COBOL programs in order to manipulate the data. By centralizing all these validation rules into the database itself, you can ensure that, no matter how your data is accessed, it will remain valid.

With DB2, stored procedures and triggers can be written in any high-level language, and can include embedded SQL, if desired. Stored procedures can even be written entirely in SQL, or by the time you read this, Java itself! The next section covers the considerations involved in calling stored procedures from Java applications using JDBC.

Stored Procedure Calls in JDBC

What are stored procedures? They are simply programs on your server that you can call via SQL statements. This means, ultimately, that the database itself actually calls the program. Stored procedures can be passed parameters, and those parameters can be updated and the new value made available to the calling program. They can also be coded to return an SQL result set to the calling program. Stored procedures are often used by enterprising programmers for two reasons:

  • They provide a standard (that is, portable) means of calling existing programs on the server.

  • They provide a terrific way to improve database-access performance versus multiple remote SQL statements.

You have already seen the SQL statement classes Statement and PreparedStatement, of which you acquire object instances via the Connection class's createStatement and prepareStatement methods. To call an OS/390 or OS/400 program object as a stored procedure, you use another statement class, CallableStatement. You acquire an object instance of this via the Connection class's prepareCall method. It is important to note that CallableStatement extends PreparedStatement, so it inherits all of its methods. Likewise, PreparedStatement extends Statement, so it inherits all of its methods.

When you call a program object as a stored procedure, you can pass in parameters to the program as follows:

  • Input-only. Read by, but not updated by, the called program.

  • Output-only. Updated by, but not read by, the called program.

  • Input-output. Read by, and updated by, the called program.

Further, your stored procedure program can optionally return one or more result sets if it is used to retrieve a list of rows from the database. Some databases support a return code from a stored procedure call, but DB2 does not. Instead, simply update one of the output or input/output parameters.

To invoke a stored procedure program from JDBC, then, follow these steps:

  1. Create a CallableStatement object by using your connection object's prepareCall method, specifying an SQL CALL statement as the parameter, and specifying a question-mark marker for each parameter (whether input, output, or both). This is done only once, and the resulting object can then be used (executed) multiple times:

    CallableStatement proc1 =
   conn.prepareCall("CALL MYLIB.MYPROG(?, ?, ?)");

  2. Specify the parameter data types and values for each input-capable (input or input-output) parameter using the setXXX methods that your CallableStatement object inherits from the PreparedStatement class:

    proc1.setInt(1, 1);
proc1.setString(2, parm2String);

    The first parameter is a relative number of the marker you are setting, and the second parameter is the value to set it to. These values remain until you change them or clear them via proc1.clearParameters();.

  3. Specify the parameter data types for each output-capable (output or input-output) parameter using the registerOutParameter method of your CallableStatement object:

    proc1.registerOutParameter(2, java.sql.Types.CHAR);

    The first parameter is the relative number of the marker you are registering, and the second parameter is the SQL type of the parameter (as listed in Table 13.10). For input and output parameters, you must do this step and the previous step, and the types specified for both must be consistent.

  4. Call the stored procedure program using the execute or executeQuery methods that your CallableStatement object inherits from the PreparedStatement class. Use the former only if your stored procedure returns multiple result sets. Use the latter if it returns one result set or no result set (in which case the result set will simply be empty):

    ResultSet rs = proc1.executeQuery();

  5. Retrieve the values of each output-capable parameter using the appropriate getXXX method of your CallableStatement object (see the CallableStatement class methods in this case) if the stored procedure program does not return a result set:

    String returnedValue = proc1.getString(2);

  6. Loop through each result set row using rs.next(). For each row, use the appropriate getXXX method of your ResultSet object (see the ResultSet class methods in this case) if the stored procedure program does return a result set:

    while (rs.next())
{
   System.out.println("Returned value: " + rs.getString(2));
}

There is one last thing to note. You cannot just call any program object this way without first registering the program in the DB2 SQL catalog. This is done using the SQL statement CREATE PROCEDURE. You only need to do this once, either from your Java program by executing a Statement object with this command, or through interactive SQL.

One common question is how to code an OS/390 or OS/400 stored-procedure program to return a result set. The answer is elegantly simple: Use embedded SQL. Use DECLARE CURSOR with a SELECT clause to define the result set, then use SQL's OPEN to open the cursor and SQL's SET RESULT SETS CURSOR cursor-name. That's it. Your COBOL program's cursor is now returned to your Java program as a JDBC result set.

Your program can both return a result set and update parameters for maximum flexibility. In fact, your program can return multiple result sets. This is handled in JDBC by using the execute method and iterating through the result sets via the methods getResultSet and getMoreResults that the CallableStatement class inherits from the Statement class.


What's New In JDBC 2.0

The previous sections describe JDBC as it was originally designed, known as JDBC 1.0. Along with JDK 1.2.0, Sun made available JDBC 2.0 (built into JDK 1.2.0). This adds some exciting new functionality to JDBC 1.0, which is summarized briefly here. We suggest you look at the html file Index.html in the JDK subdirectory \docs\guide\jdbc for more details on both the 1.0 and the 2.0 specification.

Data Type Enhancements

In JDBC 2.0, the following SQL type constants are added to the java.sql.Types class:

  • BLOB (Binary Large Objects). Often used to store graphics and other multimedia files.

  • CLOB (Character Large Objects). Often used to store documents.

  • ARRAY. An SQL array.

  • DISTINCT. A user-defined type (UDT) that is based on a built-in data type.

  • STRUCT. A UDT that can have any attributes.

  • REF. An SQL reference object.

  • JAVA_OBJECT. Used to store Java objects in your database.

To support these new types, there are new setXXX methods for substituting the markers in PreparedStatement objects and for setting the input, output, or both parameters of stored procedures in CallableStatement objects. These new methods are setBlob, setClob, setArray, and setRef. There are also corresponding new getXXX methods in the ResultSet and CallableStatement classes. Note that the DISTINCT types are really existing built-in types, so appropriate existing setXXX/getXXX methods are sufficient. Similarly, STRUCT types simply use the existing setObject/getObject methods.

To support the new get and set methods, new classes have been defined in java.sql called Blob, Clob, Array, and Ref. These are actually interfaces, not classes, but the JDBC driver suppliers implement these and supply real classes. If you decide to use these new types, have a look at the JavaDoc help for these new interfaces.

ResultSet Enhancements

The new methods shown in Table 13.14 are added to the ResultSet class. As you can see, there has been some significant capability added to result sets. You can scroll any direction, jump to any row, easily insert/update/delete the current row in the database, refresh the current row's contents from the database, and work with new data types. Phew!

Table 13.14: New ResultSet Methods in JDBC 2.0.
Method | Change | Description
absolute(int row), relative(int row_offset) | New | Moves the cursor to an explicit one-based row number, or a given number of rows forward or backward (negative number), respectively.
afterLast(), beforeFirst() | New | Moves the cursor to just after the last row or just before the first row, respectively.
cancelRowUpdates() | New | Cancels updates made to the row via new updateXXX methods but before updateRow.
deleteRow(), updateRow(), insertRow() | New | Deletes or updates the current row from the database or inserts the current row into database.
first(), last() | New | Positions the cursor at the first or last row.
getArray, getBlob, getCharacterStream, getClob, getRef | New | For support of new data types, as described in the previous section.
getBigDecimal | Updated | No second parameter for the scale anymore.
getDate | Updated | Can now specify a GregorianCalendar object as the second parameter, which is used to construct a millisecond value if the database does not store time zone information.
getObject | Updated | Can now specify a Map object as the second parameter.
getConcurrency() | New | Returns concurrency mode: CONCUR_READ_ONLY or CONCUR_UPDATABLE.
getFetchDirection(), setFetchDirection(int) | New | Returns or sets fetch direction: FETCH_FORWARD, FETCH_REVERSE, or FETCH_UNKNOWN.
getFetchSize(), setFetchSize(int rows) | New | Returns or sets the fetch size for this result set. This is a hint of the block size to the database.
getStatement() | New | Returns the Statement object that produced this result set.
getType() | New | Returns the type of this result set: TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, or TYPE_SCROLL_SENSITIVE.
isAfterLast(), isBeforeFirst(), isFirst(),isLast() | New | Returns true if the cursor is after the last row, be-fore the first row, on the first row, or on the last row, respectively.
previous() | New | Moves the cursor to the previous row.
refreshRow() | New | Refreshes the current row from the database.
rowDeleted(), rowInserted(), rowUpdated() | New | Returns true if the row has been deleted, in-serted, or updated.
updateXXX | New | Just like all the getXXX methods, but these update the value in the result set. The database is not updated until updateRow or insertRow is called.

Something New: Batch Updates

The Statement, PreparedStatement and CallableStatement classes all now support a

new method called executeBatch. This method is similar to execute, but rather than simply executing one statement represented by the statement object, it executes all the statements that have been specified with the addBatch method of the statement object. We think this is pretty cool.

The following example is from the JDBC 2.0 specification:

conn.setAutoCommit(false);
Statement stmt = conn.createStatement();
stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe Jones')");
stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')");
stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)");
// submit a batch of update commands for execution
int[] updateCounts = stmt.executeBatch();

This example was for unprepared statements, while the following is for prepared statements:

conn.setAutoCommit(false);
PreparedStatement stmt = conn.prepareStatement(
   "INSERT INTO employees VALUES (?, ?)");
stmt.setInt(1, 2000);
stmt.setString(2, "Kelly Kaufmann");
stmt.addBatch();
stmt.setInt(1, 3000);
stmt.setString(2, "Bill Barnes");
stmt.addBatch();
// submit the batch for execution
int[] updateCounts = stmt.executeBatch();

Only DDL and DML SQL statements that do not return result sets are valid candidates for batch execution. The batched statements are executed until the first one fails. To determine how many succeeded, call getUpdateCounts to get an integer array. The number of elements in the array tells you how many batch statements succeeded.

JDBC 2.0 and DB2

These enhancements require you to use JDK 1.2.0 or higher. At the time of this writing, only the OS/400 JDBC drivers support the enhancements in this new generation of JDBC (V4R4 or higher is needed for the native driver). We expect that by the time you read this, these enhancements should also available in the JDBC type-2 drivers for DB2/390 and all the DB2 family.

Optional JDBC 2.0 Enhancements

A new package called javax.sql (versus java.sql) has been designed as well, to support some advanced functionality such as database connection pools and interfaces to other optional Java initiatives like Java Naming and Directory Services (JNDS) and Java Transaction Services (JTS). This package is not part of the core JDK as of this writing, but separately downloadable from the JavaSoft Web site. It is only of value if a database vendor implements it. We expect you will see these advanced implementations as part of the Enterprise Java Beans offerings, such as in IBM's WebSphere Application Server product, Advanced and Enterprise editions.


Embedded SQL In Java: SQLJ

You have seen now the Java-supplied and industry-supported way of accessing data: JDBC. This has proven popular since Sun (with help from IBM, Oracle, and others) defined it and made it available in JDBC 1.0, with JDK 1.1.0. However, some have found it a bit unnatural for programmers accustomed to accessing their data via embedded SQL in other languages. To this end, Oracle began work on an embedded SQL specification for Java, and today it is available not as part of the standard JDK, but by database vendors wanting to offer some added value. Even though it is not part of the JDK, it is still reasonably portable as more and more database vendors step up to support it. IBM supports it across its DB2 family, for example (but not the AS/400 Toolbox for Java driver).

Embedded SQL in Java is called SQLJ. It is very similar to embedded SQL in other languages, but it is still valuable for you know to know JDBC, even if you choose to use SQLJ. SQLJ allows you to embed SQL statements inside Java. Java source code that contains embedded SQL must exist in files that end in .sqlj, versus .java, and they are preprocessed into .java files by the SQLJ preprocessor.

The manual Application Programming Guide and Reference for Java (SC26-9018) lists the following differences between JDBC and SQLJ:

  • SQLJ follows the static SQL model, while JDBC follows the dynamic SQL model.

  • SQLJ source programs are smaller than JDBC because much of the JDBC code is generated.

  • SQLJ does more data type checking during the program preparation process.

  • SQLJ allows the use of embedded Java host expressions inside SQL statements, versus separate JDBC calls for variable binding.

  • SQLJ executes statements under the authorization ID of the plan or package owner, checked at bind time. By contrast, JDBC does not know the authorization ID until runtime, and so no checking is done until then.

Although this is all true, we find that JDBC is quite easy to use, and getting better. We find the added simplicity of SQLJ to be offset by the fact it is yet another thing to learn (again, knowing JDBC anyway helps), and it requires more complexity to compile and run because of the extra steps needed to preprocess the .sqlj file into a .java file and ultimately a database plan. Obviously, we haven't cornered the market on this opinion, though, or SQLJ would not be so popular! So, it's your choice. Ultimately, SQLJ will be a better performer than JDBC because it will approach embedded SQL performance instead of that of dynamic SQL. On OS/390, it is already a better performer than JDBC.

What follows, then, is a brief introduction to SQLJ. The previously mentioned manual for DB2 on OS/390, and the Java documentation available from the OS/400 "information center" on the Internet (follow links from www.ibm.com/as400/infocenter), offer the more detailed information you'll need to actually use SQLJ.

Writing Embedded SQL in Java

To work with embedded SQL, you use the #sql syntax. Again, your source file should end with extension .sqlj, not .java. The steps for working with SQL are similar to the steps for JDBC (and some are identical). Using SQLJ involves, typically, the following steps:

  1. Import the java.sql package and the sqlj runtime packages:

    import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;

  2. Load the SQLJ JDBC driver you wish to use, for example:

    DriverManager.registerDriver(new
   com.ibm.db2.jdbc.app.DB2Driver());//AS400

    or:

    DriverManager.registerDriver(new
   COM.ibm.db2os390.sqlj.jdbc.DB2SQLJDriver() );//S390

  3. Connect to the database, and optionally assigning the connection a "context" if more than one connection is desired (thus, the last two statements of the following example are optional). Note that you probably want to turn auto-commit off:

    Connection conn1  =
   DriverManager.getConnection("jdbc:db2:MYSYSTEM");//AS400

    or:

    Connection conn1  =
   DriverManager.getConnection("jdbc:db2os390sqlj:MYSYSTEM");
   //S390

    then:

    conn1.setAutoCommit(false); // turn off auto-commit default
#sql context ctx; // declares obj ref variable for context class
ctx myConn1 = new ctx(conn1); // instantiates ctx obj using JDBC
conn'n

  4. Run your SQL non-query statements by embedding them with the #sql prefix, the optional connection context from step 3, and braces around the statement. Use traditional :variable syntax for references to Java variables, or :(expression) for Java expressions. In the following example, note that [myConn1] is optional if you have only one connection:

    BigDecimal oldRate = new BigDecimal("0.05");
String state = "PA";
#sql [myConn1] {UPDATE CUSTOMER SET RATE=:(oldRate*2) WHERE

  5. Run your SQL query statements by creating and iterating through SQLJ positioned iterators, which are similar to cursors and fetch statements in embedded SQL in other languages:

    #sql public iterator RateIter(BigDecimal, String);
RateIter   cursor1;
BigDecimal rate;
String     state;
#sql cursor1 = {SELECT RATE, STATE FROM CUSTOMER};
#sql {FETCH :cursor1 INTO :rate, :state};
while (!cursor1.endFetch())
{
   System.out.println("Rate = " + rate + ", State = " + state);
   #sql {FETCH :cursor1 INTO :rate, :state};
}

    Alternatively, use named iterators that generate get methods that match the name of the columns, for retrieval of that column's value. The syntactical difference is that you specify variable names when declaring the iterator, and use next instead of endFetch to stop the loop iteration; next advances the cursor automatically:

    #sql public iterator RateIter(BigDecimal Rate, String State);
RateIter cursor1;
#sql cursor1 = {SELECT RATE, STATE FROM CUSTOMER};
while (cursor1.next())
{
   System.out.println("Rate = " + cursor1.Rate() + ", State = " +
                     cursor1.State());
}

  6. Run your positioned update and delete SQL statements by creating an SQLJ update iterator class in one file, and use it in another (note that named iterators are used):

    // file1 (any name, but ending in .sqlj)
#sql public iterator UpdateByState implements
sqlj.runtime.ForUpdate
                        with(updateColumns="Rate");
// file2 (any name, but ending in .sqlj)
import UpdateByState;
{
   UpdateByState updateCursor;
   #sql updateCursor = {SELECT RATE, STATE FROM CUSTOMER
                        WHERE STATE='PA'};
   while (updateCursor.next())
   {
      #sql {UPDATE SET RATE=0.2 WHERE CURRENT OF :updateCursor};
   }
}

  7. Handle any errors resulting from the SQL statements. Wrap every #sqlj call in a try/catch block for SQLException:

    try{#sql …}
catch(SQLException exc)
{
   System.out.println( "SQL error: " + exc.getMessage() );
}

  8. Close the database connection when done (statements and result sets do not have to be closed):

    conn.close(); // conn is a Connection object from step 3

There is a lot more detail to be discussed with SQLJ, but we do not teach it here in the same detail as JDBC. Rather, we leave the details to you if you decide to use it.

Listing 13.8 is the same example as Listing 13.5, this time redone with SQLJ versus raw JDBC.

Listing 13.8: The example from Listing 13.5, redone with SQLJ.
Start example
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
#sql context ConnCtx1;
#sql iterator IteratorAll(String,String);
public class TestSQLJ400
{
   Connection conn;
   ConnCtx1 context1;
   static final String sysName = "mySystem"; // CHANGE THIS!
   public static void main(String args[])
   {
      System.out.println("Inside TestSQLJ400");
      try
      {
         DriverManager.registerDriver(new
            com.ibm.db2.jdbc.app.DB2Driver()); // DB2/400 Type 2
         //DriverManager.registerDriver(new COM.ibm.db2os390.sqlj.DB2SQLJDriver());

      }
      catch(Exception exc)
      {
         System.out.println("DB2 JDBC driver not found!");
         System.exit(1);
      }
      System.out.println("Class found OK");
      TestSQLJ400 test = new TestSQLJ400(); // create instance of us
      if ( test.testConnect(sysName) )
         test.testQueryAll();
      System.exit(0);
   } // end main method
   public boolean testConnect(String sys)
   {
      System.out.println("Connecting to system " + sys + "...");
      try
      {
         conn = DriverManager.getConnection("jdbc:db2://"+sys); // DB2/400 Type 2
         //conn = DriverManager.getConnection("jdbc:db2os390sqlj:"+sys); // DB2/390
         conn.setAutoCommit(false);
         context1 = new ConnCtx1(conn);
      }
      catch (SQLException exc)
      {
         System.out.println("connect failed with: '" +
                           exc.getMessage() + "'");
         return false;
      }
      System.out.println("connected ok");
      return true;
    }

   // testQueryAll method is shown in Listing 13.6

} // end class TestSQLJ400
public boolean testQueryAll()
{
   System.out.println("querying all...");
   try
   {
      IteratorAll iter;
      String      col1, col2;
      #sql [context1] iter = {SELECT * FROM QGPL.QAUOOPT};
      #sql {FETCH :iter INTO :col1, :col2};
      System.out.println("query results:");
      while (!iter.endFetch())
      {
         System.out.println(col1 + " " + col2.trim());
                           #sql {FETCH :iter INTO :col1, :col2};
      }
   } // end try
   catch (SQLException exc)
   {
      System.out.println("query all failed with: '" +
                        exc.getMessage() + "'");
      return false;
   }
   System.out.println("query done");
   return true;
} // end testQueryAll method
End example

Preparing and Compiling Embedded SQL in Java

Having finally coded a .sqlj file, you must do some more steps than you did with .java files. They are as follows:

  1. Translate the .sqlj source file into a .java source file and .ser serialized profile files (one per connection context), with the sqlj command. On OS/400, this is the Java program sqlj.tools.Sqlj, which requires the name of your .sqlj file as a parameter. It also requires that /QIBM/ProdData/Java400/ext/runtime.zip and /QIBM/ProdData/Java400/ext/translator.zip be in your CLASSPATH.

  2. Compile the .java source file into a .class file with the javac command.

  3. On OS/390 only (for now), customize the serialized profiles for DB2 into a standard DBRM with the db2profc command. Step 1 will produce xxx_SJProfile N.ser files, which are serialized Java files. There is one per connection context in your .sqlj file. (If there are no connection contexts, you still get one for the default.)

  4. On OS/390 only (for now), bind the DBRMs into a plan.

As of this writing, OS/400 only converts SQLJ to JDBC statements, but it is expected to soon support converting to database plans as is done on OS/390 (for better performance). Check the documentation, since this might be available by the time you read this. As you can see, SQLJ requires a bit more work than JDBC. Again, details are in the appropriate documentation for your operating system.


Summary

This chapter, admittedly, covered lots of different topics, including the following:

  • A brief introduction/review of COBOL and SQL database access and commitment control.

  • The architecture of JDBC for database access in Java. Java supplies a driver manager, and database vendors supply their own JDBC database drivers. You code the Java-supplied JDBC classes only; the database drivers "snap in" underneath these.

  • An overview of the steps needed to use JDBC classes in Java programs to access and manipulate DB2/400 data: load the driver, connect to the database, create statement objects, execute the statements, process the results, and close the necessary objects.

  • An overview of what is new in JDBC 2.0, the next generation of JDBC.

  • An overview of SQLJ, IBM and Oracle's support for embedded SQL inside Java.

You might not be an expert yet at programming database access in Java, but you are certainly on your way!


References

This very brief introduction to SQL, JDBC, and database programming is not intended to be all-encompassing, but rather a primer. To find out more, refer to one of the many competent references on the subject:

Accessing the AS/400 with Java. SG24-2152-00. An IBM redbook about using AS/400 Toolbox for Java and VisualAge for Java to access AS/400 data and programs.

Application Programming Guide and Reference For Java (S/390). SG26-9018.

AS/400 Client/Server Performance Using the Windows Clients. SG24-4526-01. An IBM redbook covering performance-tuning the AS/400 database server program:

AS/400 Information Center. Either look up SK3T-202701 on the CD-ROM that comes with the system, or http://www.ibm.com/as400/infocenter.

AS/400 Java home page: http://www.ibm.com/as400/java.

Conte, Paul. 1997. Database Design and Programming for DB2/400. Duke Press. ISBN 1882419065. For AS/400 database and SQL programming information.

DB2 Product Family home page: http://www.ibm.com/software/data/db2.

e-business Application Solutions on OS/390 Using Java: Volume 1. SG24-5342.

Hamilton, Cattell, and Fisher. 1997. JDBC Database Access with Java: A Tutorial and Annotated Reference. Addison-Wesley, ISBN 0-201-30995-5

Integrating Java with Existing Data and Applications on OS/390. SG24-5142. An IBM redbook covering all manner of OS/390 and Java access.

S/390 Java home page: http://www.ibm.com/s390/java.

S/390 Online Library: http://www.ibm.com/s390/reference.


Chapter 14: More Java

Overview

At this point, having read the previous chapters, you have seen almost all the syntax for the Java language. It is important to note that while Java changes dramatically from version to version, almost all those changes involve the introduction of new Java packages, classes, interfaces, and methods in the JDK. The core syntax of the language is very stable, with the last major revision being the introduction of inner classes "way back" in JDK 1.1.0.

We want to ensure that this book gives you both a complete introduction to all the syntax in the Java language, and an introduction to all the major functionality in the language via JDK-supplied packages. The latter is reviewed shortly, but first there are some basic syntactical aspects of the language that you haven't seen yet, mainly because the majority of COBOL programmers moving into Java will probably not need to know them. For completeness, however, a very brief tour of those remaining syntax details is offered here. Syntax, in this sense, means any functionality that requires a modifier, keyword, operator, or other basic element in the language.


Inner Classes

Chapter 2 explains that Java applications contain packages, which contain classes (or interfaces), which contain static and instance variables and methods. Methods, in turn, contain parameters, local variables, and finally executable code. Chapter 2 does not mention, however, that classes themselves can contain other classes. That is, just as COBOL programs can contain nested COBOL programs, so too can Java classes contain subordinate classes nested inside them. A simple example is shown in Listing 14.1.

Listing 14.1: A Java class containing other subordinate classes.
Start example
public class Dept
{
   private int      deptNbr;
   private Emp      deptMgr;
   private Vector   deptEmps = new Vector();

   public Dept(int nbr, Emp mgr)
   {
      deptNbr = nbr;
      deptMgr = mgr;
   }

   // Dept methods not shown

   /** Return enumeratable list of employees */
   public Enumeration getEnumeration()
   {
      return new EnumerateDept();
   } // end method getEnumeration

   /** Inner Class For Enumeration Object */
   class EnumerateDept implements Enumeration
   {
      private int pos=0;

      /** end-of-list test... */
      public boolean hasMoreElements()
      {
         return (pos < deptEmps.size());
      }
      /** retrieve next element... */
      public Object nextElement()
      {
         Object retobj = null;
         if (hasMoreElements())
         {
            retobj = deptEmps.elementAt(pos);
            pos = pos+1;
         }
         return retobj;
      }
   } // end EnumerateDept inner class
} // end Dept class
End example

The full example of this class plus its helper Emp class are not shown here (although they are on the CD-ROM). The key part of this class is its nested or inner class, EnumerateDept. It is declared right inside the "outer class," Dept, which is the only class that ever needs to see it. The Dept method getEnumeration returns an instance of it, but defines the return type to be the interface Enumeration that the inner class implements.

Inner classes bring nothing new to the table. They offer no functionality that you can't get by simply coding the same class by itself, outside of its nested class. This is the same for COBOL nested programs. The reason for their existence is programmer convenience. They save the trouble of working with separate classes, and hence separate source files, for those classes that are truly nothing more than implementation details.

Inner classes have access to all the variables of the containing class, including those marked private. The opposite is not true; the containing class cannot access the variables of the inner class. Further, inner class instances are scoped to the current instance of the containing class in which they were instantiated. They can directly access the instance variables of the outer class, and can use the syntax "this.this" to refer to the outer class instance they are contained in.

Inner classes can also be defined inside blocks or methods. In these cases, they can also access the local variables and method parameters of their enclosing block or method. Java insists that you mark the local variables and methods final if you want to allow the inner classes to be able to access them. This is because an instance of the inner class might "live on," long after the block or method has completed execution. Thus, the value of the referenced local variables and parameters need to live on as well, and that means they need to be non-changing.

Listing 14.2 shows a modified version of Listing 14.1, in which the inner class has been moved to be directly inside the only method that uses it. In addition to moving the inner class to be entirely within the getEnumeration method, a local and final variable called empsCopy is defined to hold a copy of the current contents of the employee Vector. The inner class code is then changed to use this copy. This is a good idea, since the returned enumeration object should be impervious to changes made to the department after it gets the object. It should be a snapshot in time. In this example, the same effect could have been achieved by declaring empsCopy as an instance variable of the inner class.

Listing 14.2: An inner class directly inside the method that uses it.
Start example
public class Dept2
{
   // … same …
   public Enumeration getEnumeration()
   {

      final Vector empsCopy = (Vector)deptEmps.clone();

      class EnumerateDept implements Enumeration
      {
         private int pos=0;

         // end-of-list test...
         public boolean hasMoreElements()
         {
            return (pos < empsCopy.size());
         }
         // retrieve next element...
         public Object nextElement()
         {
            Object retobj = null;
            if (hasMoreElements())
            {
               retobj = empsCopy.elementAt(pos);
               pos = pos+1;
            }
            return retobj;
         }
      } // end EnumerateDept class
      return new EnumerateDept();
   } // end method getEnumeration
} // end class Dept2
End example


Anonymous Inner Classes

In an effort to save even more typing, Java also allows you to define an inner class right on the new operator. You simply place the class definition immediately after the new statement, just before the semicolon. In this case, you don't even have to name the class, hence the term anonymous class. Listing 14.3 is another revision of the example to use an anonymous inner class instead of a named inner class.

Listing 14.3: An example that uses an anonymous inner class.
Start example
public Enumeration getEnumeration()
{
   final Vector empsCopy = (Vector)deptEmps.clone();

   return new Enumeration()
   {
      private int pos=0;

      // end-of-list test...
      public boolean hasMoreElements()
      {
         return (pos < empsCopy.size());
      }
      // retrieve next element...
      public Object nextElement()
      {
         Object retobj = null;
         if (hasMoreElements())
         {
            retobj = empsCopy.elementAt(pos);
            pos = pos+1;
         }
         return retobj;
      }
   }; // end anonymous inner class Enumeration
} // end method getEnumeration
End example

Notice that the new operator in this case names the existing class or interface to create a new instance of, followed by a class body that ends with a semicolon. The name given on the new operator must be a class or interface that already exists. If it is a class name, the subsequent class body is considered to extend that class. If it is an interface name, the subsequent class body is considered to implement that interface and extend java.lang.Object.

The keywords extends and implements are not permitted on anonymous classes. Further, they are not permitted to define constructors. If any parameters are given between the parentheses on the new operator, they are passed to the parent's matching constructor.

Anonymous classes are most often seen in GUI and Java Bean coding, where you want to register your interest in a particular event. For example, you saw in chapter 12 how addActionListener is used to register GUI components for action events, and how the ActionListener interface is typically implemented in the class, with an action Performed method that checks for the particular component that fired the event. Alternatively, for each addActionListener call, you could have used new with an anonymous class to pass an instance of an anonymous class that implemented the actionPerformed method for just that component, as follows:

okButton.addActionListener( new ActionListener ()
{
   public void actionPerformed(ActionEvent evt)
   {
      System.out.println("OK pressed");
   }
}; );

Inner classes, both named and anonymous, should only be used for very small classes that can be digested at a quick glance. If they are large, they actually have a negative effect on the readability and maintainability of the code, completely defeating their purpose. Some programmers don't like to use them at all, but you will see them used heavily in the JDK documentation and examples.


Static Initializers

You have seen static variables, or class variables, from chapter 2 on. These are class-level variables defined with the modifier static to indicate they have one memory location for the life of the application, versus one per instance of the class. Recall that you can initialize static variables, like all variables, at the time they are defined, as follows:

static int nextAvailableID = 100;

When the JVM first loads your class into memory, it immediately allocates storage for all the static variables in the class and then runs the initialization expression, if there is one (otherwise, it assigns the default initial value based on the variable type). Quite often, there are other important things to do at that initial-load time as well. For example, you might want to open a connection to a database, read an initial value from a file, and so on. Java allows for this by allowing you to define any block of code at the class level (i.e., not inside a method) inside the usual curly braces, as long you preface the block with the keyword static, for example:

static { MyOtherClass.doStuff(); }

The JVM runs these blocks of code at the time the class is first loaded into memory.

Any references to methods must, of course, be static methods, unless you include code to instantiate an object, as usual. These blocks of code are called static initializers, and they are the static equivalent of a constructor. The alternative to a static initializer is simply a static method, to which you explicitly code a call at the beginning of your application. Because static initializers are run so early in an application, there are restrictions on exactly what you can do in them. For example, you can't call any methods that throw exceptions unless you use try/catch to monitor for those exceptions.

In general, you don't see too many people coding elaborate static initializers. They tend to heavily clutter a class. Further, it just doesn't sit well with some people to write executable blocks of code outside of a formal method. As with anonymous classes, the increment and decrement operators, and the ternary operator, you can avoid using them, but you probably can't avoid seeing them.


Java Native Interface (JNI)

We hate to admit it, but occasionally there are times when there is something you just cannot do in Java, and you are forced to revert to another language, such as C. As the packages supplied with the JDK get increasingly richer, this becomes less and less of an issue, but Java does have built-in support for calling out of the language.

One option is to call an external executable object, which is done using a supplied method and covered shortly in this chapter. This is not very efficient, though, and it does not allow you to exchange parameters. The alternative is to call a function in a DLL (or service program on OS/400). This runs in the same address space as the virtual machine, so it is usually more efficient and offers more opportunity to interoperate with the called function.

Java comes from Sun with built-in support for calling C functions in DLLs, using something referred to as Java Native Interface or JNI. This is done by wrapping the C function in a Java method. You have to code a Java method somewhere that uses the modifier native. This modifier is similar to abstract, in that when you use it, you don't define a method body:

public native String getEnvironmentVariable(String name);

The modifier native tells Java that this method is not implemented in Java at all, but rather is implemented in C (usually) in a DLL somewhere. What DLL, exactly? Well, you have to tell Java that, by coding a static initializer (now you know why these exist) in your class that loads that DLL into memory:

static { System.loadLibrary("MyDLL"); }

This will use the static method loadLibrary in the System class of the java.lang package to find the named DLL on disk and load it into memory. What function in the DLL is called when someone calls your native method? The function with the same name as your native method name. What parameters are passed to it? The same parameters that were passed to your native method. Of course, some data-type mapping has to occur between the Java language data types and the C language data types, and that is done for you automatically. These mappings are well-defined and documented in the JDK documentation. Can the C function update the parameters? You bet! It is quite cool how Java does all of this underlying work for you, so seamlessly.

So, you "don't do C"? Well, not to worry. You can call any DLL (or service program on OS/400) written in any language that is compatible with C. That is, if you can call the function in that DLL from C, then you can call it from Java. Yes, that includes COBOL, too. For OS/390, you can only call exported COBOL programs that are inside DLLs, not executable load modules. For OS/400, you can only call exported COBOL ILE procedures inside service programs, and only as of V4R4 when the ILE COBOL compiler runtime was made thread-safe. Of course, on both systems, you could also call a C function that in turn calls a COBOL program.

JNI involves some other stuff, too. For example, it specifies a set of C APIs that can be called to go the other way. That is, from a C program (or any program that can call C APIs), you can instantiate a Java class and call methods on that class. These APIs are a core part of the JDK, so while they are not written in Java, all operating systems that support Java are required to support them, as well.

There is much more to know about JNI, but to get the details, refer to the JDK documentation in \jdk1.2.1\docs\guide\jni\index.html. We don't encourage you to use JNI too heavily because it will clearly get in the way of your portability. Also, there are other options that are actually easier. For example, on OS/400, there is the AS/400 Toolbox for Java that makes calling COBOL programs (both OPM and ILE) a snap (see appendix E). You can also use JDBC to easily invoke any COBOL program as a stored procedure (see chapter 13). For OS/390, there are also connectors to allow you to invoke existing CICS and IMS transactions from Java (see appendix D). All of these are better, higher-level, alternatives to "down and dirty" JNI coding.


Java Remote Method Invocation (RMI)

Imagine that you have Java code running on a client (such as Windows or a Web browser), and you want to call Java code running on a server. Perhaps that server Java code uses JNI or another option for accessing an existing application or transaction, and you want to get the results down to the client for display to the user. The options for this very common requirement are as plentiful as the options for accessing existing COBOL discussed in the previous section. The Java-designed and -defined way of doing this is via something known as Java Remote Method Invocation, or RMI. RMI is a means to do a remote procedure call (RPC), but in an object-oriented way.

In the days of client/server, RPC was a popular way of remotely calling a function on the server, from the client (as defined in the Distributed Computing Environment, or DCE, world). In an OO world, the preferred way to do client-to-server communications is via distributed objects. This is a simple concept: a distributed object lives on the server, but has a thin proxy that is for client code. This proxy object appears to have all the same methods as the "real" object on the server, but when client code calls one of those methods, the call is actually shuttled to the server, where it is executed using the real object. Any updated parameters or returned values are returned to the caller. Preferably, the client code does not know or care that the object it appears to be working with is actually a proxy.

To do distributed object programming, somebody has to give you the engine to enable the "magic" shuttling across the network (officially called marshalling). This, for example, is what OMG's (Object Management Group) CORBA (Common Object Request Broker Architecture) is all about, and what Microsoft's DCOM (Distributed Common Object Model) is all about. Java's answer is RMI, and it is a core part of the JDK, hence it is available on every system that supports Java. By the way, a client can be any operating system, Web browser, or device that supports Java. And so can a server. It is up to you to decide what's the client and what's the server, although the server is almost always the system where the data, applications, or transactions are located.

To use RMI to create a distributed object, you must perform these steps:

  1. Create an interface that describes the methods you want to make available remotely on clients. This interface must import java.rmi.*, and extend Remote (from java.rmi). Further, every method signature must be defined with throws RemoteException.

  2. On the server side, create a class that implements your interface, and code it as usual to perform the functions you want. As per interface rules, you must supply implementations for all the methods in the interface. Your class must import java.rmi.* and java.rmi.server.*, and extend UnicastRemoteObject from package java.rmi.server. (It's okay if you can't extend, but you have to do some extra work in your constructor.) Your class must supply a main method that instantiates an instance of this class and does some work to make that object distributable. This includes giving the object a name that clients will refer to (since classes can and usually do have multiple objects, and clients work with objects, not classes, each object needs a unique name), and registering the object in the RMI registry. This is done using the static method rebind in the Naming class in package java.rmi. Prior to this, you must also call the setSecurityManager method in the System class, and pass it an instance of RMISecurityManager. By convention, you name your class the same as the interface, but append "Impl" (for implementation) to the end of the name.

  3. On the client side, for code that needs to use the distributed object, you declare an instance variable to be the interface type from step 1. Then, rather than using new to instantiate your class from step 2, you call the static method lookup in class Naming in package java.rmi. This method takes a URL that defines the name or IP address of the remote system containing the remote object, and the name given your server class object in step 2. It gives you back the reference to a proxy of the server class object by actually downloading that proxy to the client virtual machine. It finds this object on the server by looking in the RMI registry on the server, using the name as a key. Now, you simply call the methods in the proxy as though it were a normal local object, but taking care to monitor for RemoteException exceptions.

So, where does the proxy of your server class come from? You create it using the rmic tool from the JDK. Give it your class name, such as XXX, and it will give you back two class files: XXX_stub.class and XXX_skel.class. The former is the proxy for the client, and the latter is the gorp or skeleton version of the server class, which does all the ugly communications code for each method. You simply place both of these on your server, and RMI takes care of finding and using them appropriately. You never refer to them explicitly in your code.

What about that RMI registry? It is executable code that you must start running on your server for all this to work. To start it, simply enter the JDK command rmiregistry. Once running, it waits for requests from server Java applications to register objects (with the Naming.rebind method) and from client Java applications or applets to find and retrieve object stubs (with Naming.lookup).

Once the registry is running, you can start your server-side class running by calling it from the command line using the java command as usual. Unlike the usual way of running Java classes from the command line, you have to specify some options on the Java command line. Specifically, you have to use the -D option to specify a few important properties. These are the directory to find the distributable Java classes in, and a security policy file:

java -Djava.rmi.server.codebase=http://myhost/myclasses/
      -Djava.security.policy=d:/rmi/mysrc/policy examples.hello.HelloImpl

The codebase property lists the class's directory using a URL syntax that includes the name or IP address of the host server. The policy property fully qualifies a policy file that is needed by the security manager. Here is an example of a policy file that gives all classes access to all distributed server objects:

grant {
   // Allow everything for now
   permission java.security.AllPermission;
};

Once your Java server class runs, its main method will set the security manager (a necessary detail), instantiate an object, and register it in the registry, as discussed in step 2 and shown here (taken from the Hellolmpl.java JDK-supplied RMI tutorial):

// create and install a security manager
if (System.getSecurityManager() == null)
   System.setSecurityManager(new RMISecurityManager());
try {
   HelloImpl obj = new HelloImpl();
   // Bind this object instance to the name "HelloServer"
   Naming.rebind("//myhost/HelloServer",obj);
   System.out.println("HelloServer bound in registry");
} catch (Exception exc) {
   System.out.println("HelloImpl err: " + exc.getMessage());
}

This makes the object available to client code, such as following:

Hello remoteObj = null;
   // Hello.java is the remote object interface
try {
   remoteObj = (Hello)Naming.lookup("//myhost/HelloServer");
} catch (Exception exc)
   System.out.println("Remote communication failed with: " +
      exc.getMessage());
   System.exit(0);
}
System.out.println(remoteObj.sayHello());

Note that only the initial object stub typically needs to be remotely downloaded using lookup, and hence using the registry. After that, methods in the remote server object can return instantiated objects to the client code as they would normally return objects to local code.

Examples and further details of RMI are not covered here, since there is sufficient information in the JDK documentation. For these details, see the file c:\jdk1.2.1\docs\guide\ rmi\index.html where c:\jdk1.2.1 is the directory in which you installed the Windows JDK. (This assumes you have installed the JavaDoc documentation for the JDK as well.)

What is important for you to take from this introduction is that RMI is a relatively easy way to write distributed objects in Java. It is a great way to write code that runs on a server and hence has easy access to all your resources there, and then to distribute that code to a client, such as a Web browser, where it can be displayed to the user. The underlying communications protocol is TCP/IP by default, but RMI does allow you to choose IIOP (Internet Inter-Orb Protocol) for CORBA. If you are only interested in doing Java-to-Java communications (versus, say, Java to C++), this is an easy way to achieve it.


Java Object Serialization

If you think about the RMI discussion in the previous section, you will realize just how much work is being done for you behind the scenes. This is especially true when client code calls a remote method and passes objects as parameters. How does it pass these objects? Well, there really is only one way to pass data between any client and server: as a stream of bytes. That means all object parameters must be converted or flattened to a stream of bytes first, then sent across the network to the server method, which in turn must convert or "unflatten" the stream of bytes into an object. The conversion from an object to a byte stream and back again is referred to as serialization.

It gets even more complicated with objects that contain other objects as instance variables. Each contained object, and every object inside the contained objects, must be serialized in order to send the entire main object across the network. The other side, the server, must in turn know exactly the algorithm used to do this, so it can do exactly the opposite to re-create the contained objects and the main object from the byte stream.

Serializing an object to a byte stream, and de-serializing it from a byte stream, is not only of value in RMI communications. Often, you will need to save the current state of an object to disk, and then retrieve it from disk later. The "old" way of doing this was to create your own code to write each of the variables out to disk manually, using your own convention. For example, you might use a special character to denote the end of each element in an array, so when you subsequently read it in again, you know where each record ends. The built-in Java support for serialization makes saving the data in any object, and subsequently restoring it, trivial. The official term for this is persistence. Via serialization, you persist an object to disk.

Whether serializing an object to pass as a parameter in an RMI method call, or to persist it to disk, the important step is generating the byte stream. After that, it is a simple matter to send the stream to disk or over the network. So how do you produce this stream? For any class you write that you want to be able to serialize, you must define the class to implement the Serializable interface from package java.io. This interface contains no methods, it is only used as a flag to the compiler and runtime. To actually write your object to disk, then, you use the ObjectOutputStream class from the java.io package.

Instantiate it using a FileOutputStream object for the target file (a class from java.io for writing streams to disk), and then use the writeObject method to write objects to the stream, and therefore to the file. After writing, call flush to force the buffer to disk, and then close the file output stream.

For example, Listing 14.1 showed a classed called Dept. You can see (by opening it from the CD-ROM) that it has a main method, which creates an instance of the class and populates it with data. We renamed that class to DeptSerializable and implemented the Serializable interface:

import java.io.*;
public class DeptSerializable implements Serializable

The only wrinkle is that all instance variable objects in the class must also be of a serializable class type. Thankfully, almost all the JDK-supplied classes are serializable. The Dept class contains instances of another class, Emp, which also had to be changed to implement Serializable, and which was renamed to EmpSerializable. Next, to test writing to disk, the main method of DeptSerializable was changed to serialize the instance of DeptSerializable it creates and populates. It was serialized to file Dept.ser, using a FileOuputStream object and an ObjectOutputStream object, and calling the method writeObject in ObjectOutputStream, as shown in Listing 14.4.

Listing 14.4: The main method of the DeptSerializable class.
Start example
public static void main(String args[])
{
   int mgrDeptId = 100;
   int empDeptId = 200;
   EmpSerializable  mgr = new EmpSerializable(1, "Jim Bean", mgrDeptId);
   DeptSerializable dept = new DeptSerializable(empDeptId, mgr);
   dept.addEmployee(new EmpSerializable(20, "George Farr"      ,empDeptId));
   dept.addEmployee(new EmpSerializable(20, "Phil Coulthard"   ,empDeptId));
   dept.addEmployee(new EmpSerializable(21, "John S Page"      ,empDeptId));
   dept.addEmployee(new EmpSerializable(22, "Sam Let"          ,empDeptId));
   dept.addEmployee(new EmpSerializable(22, "Eli J Bean"       ,empDeptId));

   try
   {
      FileOutputStream ostream = new FileOutputStream("Dept.ser");
      ObjectOutputStream serStream = new ObjectOutputStream(ostream);
      serStream.writeObject(dept);
      serStream.flush();
      ostream.close();
      System.out.println("Dept written to Dept.ser");
   }
   catch (Exception exc)
   {
      System.out.println("Error serializing: " + exc.getMessage());
      exc.printStackTrace();
   }
} // end main method
End example

Note the serializing code is enclosed in a try/catch block because it can throw exceptions. When you run this class, it runs to completion, and you now have a file called Dept.ser in your current directory.

Next, let's test reading it in from disk. You do this using the functional opposites of everything: the FileInputStream class, ObjectInputStream, and readObject, as shown in Listing 14.5.

Listing 14.5: Re-instantiating an object serialized to disk.
Start example
import java.util.*;
import java.io.*;

public class TestDeptSerializable
{
   public static void main(String args[])
   {
      try
      {
         FileInputStream istream = new FileInputStream("Dept.ser");
         ObjectInputStream serStream = new ObjectInputStream(istream);
         DeptSerializable dept = (DeptSerializable)serStream.readObject();
         istream.close();
         printDept(dept);
      }
      catch (Exception exc)
      {
         System.out.println("Error reading from disk: " + exc.getMessage());
      }
   } // end main method

   public static void printDept(DeptSerializable dept)
   {
      Enumeration allEmps = dept.getEnumeration();
      System.out.println();
      String hdr = "Employees of dept " + dept.getNbr() + ":";
      System.out.println(hdr);
      for (int idx=0; idx<hdr.length(); idx++)
         System.out.print('-');
      System.out.println();
      System.out.println(dept.getMgr().getName()+" (mgr)");
      while (allEmps.hasMoreElements())
      {
         EmpSerializable currEmp = (EmpSerializable)allEmps.nextElement();
         System.out.println(currEmp.getName());
      }
   }
} // end TestDeptSerializable class
End example

Notice that a printDept method is supplied and called to verify that all the data was read in properly. Running this gives the following:

Employees of dept 200:
-----------
Jim Bean (mgr)
George Farr
Phil Coulthard
John S Page
Sam Let
Eli J Bean

You must admit, this is pretty good. It is a very easy way to save state or data persistently between runs of an application, with only five lines of code to save it and four lines of code to restore it (not including the try/catch code). You could also use it with the JDK-supplied classes for writing TCP/IP or HTTP communications code, for sending objects across the network. Note that all of this is done for you when using RMI. By the way, if you have variables that are sensitive (such as password or salary), you can use the transient modifier on them. Any transient variables are not saved when their enclosing object is serialized, and they are set to their type's default value when the object is de-serialized. There are other capabilities in Java's serialization support, such as dealing with parent classes that are not serializable, and dealing with multiple versions of an evolving class.

For more details on serialization in Java, see the JDK-supplied documentation in the file c:\jdk1.2.1\docs\guide\serialization\index.html, where c:\jdk1.2.1 is the directory where you installed the Windows JDK.


Java-Supplied Packages

Finally, then, let's review the Java packages that offer all the "out-of-the-box" functionality beyond the core language capabilities. After that, we will touch on a number of topics that transcend the JDK-supplied capabilities.

The Java language is often referred to as a platform because the intention is for you to write code that is operating system independent. Keep in mind that the Java "compiler" (javac) generates bytecode for the Java Virtual Machine. It does not generate machine code for the specific machine it happens to run on. Furthermore, to achieve the Java "write once, run anywhere" goal, you need to be able to write Java code that successfully avoids calling operating system APIs or services. That is certainly the case for applets. Applets are not even permitted by the Java security manager to call out to system services and APIs, or to any local program or DLL. Java applications and Servlets have more options, of course, including the option to invoke native system commands and C code.

Again, the intent is to decrease (as much as possible) your dependence on the underlying operating system. That will increase the portability of your code. To this end, the Java language must supply the programmer as much functionality as possible to avoid the need to call operating system services. This is the root of the reference to Java as a platform: programmers are programming to Java and its set of supplied functions. This is in contrast to the traditional style of programming to the target operating systems' APIs.

By now we are sure you will agree that Java deserves credit for being a reasonably good language because it has the power of object orientation but is not too complex. On top of this base, further functionality is provided by the class libraries, or packages, that are supplied with the language, as shown in Figure 14.1. This shields you from the need to "break out" (access the operating system). The richer these packages are (and they are getting richer with each release), the more functionality you have built-in. Another big advantage is that, because the packages are ported everywhere with the JVM, you can depend on having an impressive amount of functionality available to you wherever you go.

Figure 14.1: The Java platform.

You are already familiar with some of the class libraries or packages that follow the naming convention "java.xxx." For example, you have seen java.sql (for JDBC) and java.math (for BigDecimal), as well as java.awt, java.awt.event, and java.applet (for user interfaces). You have also used java.util for the Date, HashTable, and Enumeration classes, and java.text for SimpleDateFormat. You have used many of the classes in java.lang, the "default" package that is automatically imported by Java, and you have seen such classes as java.lang.Object, java.lang.Class, java.lang.Thread, and java.lang.String. Table 14.1 lists the interesting "core" packages that are supplied with Java at the 1.1.x level.

Table 14.1: JDK 1.1.x Packages.
Package | Description
java.lang | Base set of language classes, like Object and Class.
java.util | Utility "value-added" classes, like Vector, Date, and HashTable.
java.awt | Abstract windowing toolkit, user interface components.
java.awt.event | Abstract windowing toolkit, user input events.
java.applet | Base Applet class for creating applets that run in a Web browser.
java.image | Classes for working with images, such as .gif files.
java.text | Internationalization classes for writing locale-sensitive applications.
java.sql | Framework for JDBC drivers, which is implemented by database vendor supplied JDBC drivers.
java.io | Classes and interfaces for working with streams, such as flat files.
java.math | Math classes. (Hey, just like high school!)
java.net | Networking classes for those interested in writing their own communications code, for example. Makes TCP/IP and HTTP communications relatively easy.
java.rmi | Remote method invocation, the object-oriented way to write distributed applications: split your class in two identical pieces-a proxy for the client and a server where the work is actually done. See docs/guide/rmi/index.html in your JDK documentation.
java.security | Framework for implementing digital signatures and message digests, with more to come. See docs/guide/security/index.html in your JDK documentation.

Table 14.2 lists the new packages added as of JDK 1.2.0. The packages we have not described are those that are probably not be relevant to your first business application, or to a Java GUI front-end, or to your existing COBOL application. The purpose of this book is to provide you with a "COBOL slant" on Java that will serve you well if you choose to dig deeper by consulting other books that cover Java packages in more detail.

Table 14.2: New Packages in JDK 1.2.x.
Package | Description
java.awt.dnd | For drag-and-drop support.
java.awt.font | Provides support for Type 1 and 2 Multiple Master fonts, and OpenType and TrueType fonts.
java.awt.geom | Provides 2D geometry for advanced graphics manipulation.
java.awt.im | Input Method framework for alternative DBCS input.
java.awt.print | General printing classes and interfaces. java.awt.image.renderable For creating rendering-independent images.
java.beans.beancontext | A bean context is a container for beans and defines the execution environment for the beans it contains.
java.lang.ref | Provides weak references to objects so they'll still be garbage collected.
java.util.jar | Reading and writing JAR files.
java.rmi.activation | For activating a persisted remote object.
java.security.cert and spec | For parsing and managing certificates, and for "key" specifications and algorithm parameter specifications.
javax.swing and javax.swing.xxx | New User Interface controls, layout managers, and models, plus supporting packages. See chapter 12 for more detail.
org.omg.CORBA and org.omg.xxx | Mapping of OMG CORBA to Java, including an ORB, plus supporting packages.


Common "How Do I?" Questions

To round out your introduction to Java, the following pages answer typical questions that we have been asked by COBOL programmers who are learning Java, or that arose as we were learning and writing in Java, ourselves. Although this information refers to packages and/or classes not included in this book, once you know where to look, you can easily refer to the online documentation.

Note that the questions are answered in the context of Java running on Windows. For Java running on OS/390 or OS/400, the answers are the same, but are pertinent to the Hierarchical File System (HFS) on OS/390 and the Integrated File System (IFS) on OS/400, instead of the Windows file system.

For OS/400 programmers who want to have similar functionality when accessing their IFS file system from a remote client, refer to the AS/400 Toolbox for Java, which offers IFS classes that extend these built-in JDK classes, but which work against the OS/400 Integrated File System, even when run from a remote Windows or any other client.

How Do I Determine the Working Directory?

Use the System.getProperty("user.dir") static method call, as shown in Listing 14.6. This returns a String object that contains your current working directory.

Listing 14.6: Determining the working directory with the System.getProperty ("user.dir") static method call.
Start example
public class TestWorkingDir
{
   public static void main(String args[])
   {
      String currDir = System.getProperty("user.dir");
      System.out.println("Current directory: " + currDir);
   }
}
End example

Compiling and running this gives the following:

C:\JAVA\misc>java TestWorkingDir
Current directory: C:\JAVA\misc

How Do I Change the Working Directory?

You have to set the system property user.dir. The only way to do this prior to JDK 1.2.0 is to retrieve all of the properties, change user.dir, and then set all of them. This is because, surprisingly, Java did not supply a setProperty method until JDK 1.2.0. There was only a setProperties method that set all of them, as shown in Listing 14.7.

Listing 14.7: Setting a working directory in JDK 1.1.x.
Start example
public class TestSetWorkingDir
{
   public static void main(String args[])
   {
      System.out.println("Was: " + System.getProperty("user.dir"));
      java.util.Properties sysProps = System.getProperties();
      sysProps.put("user.dir", "c:\\phil");
      System.setProperties(sysProps);
      System.out.println("Is : " + System.getProperty("user.dir"));
   }
}
End example

Notice the double backslash. Because the backslash is an escape character in Java strings, you must use a double backslash to get just one. This situation is similar to what you find in the C and C++ languages. Compiling and running this example gives the following:

C:\JAVA\misc>java TestWorkingDir
Was: C:\JAVA\misc
Is : c:\phil

In JDK 1.2.0 of later, you can just use

System.setProperty("user.dir","c:\\phil");

We recommend that you avoid the hard-coded backslash character here because not all file systems use it. For example, on UNIX, OS/390, and OS/400, you would need to use a forward slash. A good portable class will use the Java-supplied constant path SeparatorChar in class File of package java.io. This will be defined properly for each system on which you run your Java application. Keep in mind that applets are not permitted to change the working directory.

There are other system properties you can query with System.getProperty and set with System.setProperty. To see a list of them, look at the JavaDoc help for the getProperties method in the System class of the java.lang package.

How Do I List Files and Directories?

To list files and directories programmatically, use the java.io.File class. It's worth your while to explore this rich class. The example in Listing 14.8 lists all files in the current working directory.

Listing 14.8: Listing all files in the current working directory.
Start example
import java.io.File;
public class TestList
{
   public static void main(String args[])
   {
      String currDir = System.getProperty("user.dir");
      File listFiles = new File(currDir);
      String[] allFiles = listFiles.list();
      for (int idx = 0; idx < allFiles.length; idx++)
         System.out.println(allFiles[idx]);
   }
}
End example

This gives the following:

C:\JAVA\misc>java TestList
   TestList.java
   TestWorkingDir.java
   TestWorkingDir.class
   TestList.class

To subset the list, you can specify a FilenameFilter object on the list method call. You must create your own class that implements this interface, and code in an accept method that takes as input a directory name and a file name. Java will call this method for each file in the directory, and you have to decide whether to return true, indicating the name should be considered part of the list. For example, to subset the list to retrieve all .java files, you would use the class in Listing 14.9.

Listing 14.9: A name filter class to show only names ending in .java.
Start example
import java.io.*;
public class JavaFileFilter implements FilenameFilter
{
   public boolean accept(File dir, String name)
   {
      String lcName = name.toLowerCase();
      return (lcName.endsWith(".java"));
   }
}
End example

When calling the list method, the next step is to specify an instance of this class:

String[] allFiles = listFiles.list(new JavaFileFilter());

Now, the previous TestList class gives a subset:

C:\JAVA\misc>java TestList
   TestList.java
   TestWorkingDir.java
   JavaFileFilter.java

To list files and directories in a user interface using AWT, use the supplied java.awt.FileDialog class. Do not spend too much time learning the setFilenameFilter method, because it does not work. Listing 14.10 is an example that is usually tied to a Browse push button.

Listing 14.10: Displaying an AWT Open dialog.
Start example
   public static void showOpenDialog(Frame parentWindow, String path,
                                       TextField pathEntry, TextField
fileEntry)
   {
      FileDialog browseDlg =
         new FileDialog(parentWindow, "Select File", FileDialog.LOAD);
      browseDlg.setDirectory(path);
      browseDlg.show();
      String newDir  = browseDlg.getDirectory();
      String newFile = browseDlg.getFile();
      if ((newDir != null) && (newDir.length()>0))
         pathEntry.setText(newDir);
      if ((newFile != null) && (newFile.length()>0))
         fileEntry.setText(newFile);
   }
End example

For the constructor, it is necessary to specify the parent window, the title of the dialog, and the mode. The mode is either FileDialog.SAVE or FileDialog.LOAD. If it is SAVE, users will get a warning if they select an existing file. Java uses the underlying file system's Open dialog for this class. On Windows NT, this produces the typical Open dialog shown in Figure 14.2. You can test this example by running the supplied sample TestFileDialog and clicking the List button.

Click To expand
Figure 14.2: The AWT Open/Save dialog (FileDialog).

To do the same thing in Swing (versus AWT), you would use the new JFileChooser class. This gives you a dialog that is totally authored in Java, and thus looks and behaves identically on all systems. As mentioned in chapter 12, you can decide on that look and feel, with the choices currently being Metal (native Java), Windows, or Motif (UNIX). Listing 14.11 shows an example similar to Listing 14.10, but this time using Swing instead of AWT.

Listing 14.11: Displaying an Open dialog using Swing.
Start example
public static void showOpenDialog(JFrame     parentWindow,
                                  String     path,
                                  JTextField pathEntry,
                                  JTextField fileEntry)
{
   JFileChooser browseDlg =
      new JFileChooser();
   browseDlg.setCurrentDirectory(new java.io.File(path));
   int button = browseDlg.showOpenDialog(parentWindow);
   //int button = browseDlg.showSaveDialog(parentWindow);
   if (button == JFileChooser.APPROVE_OPTION)
   {
      java.io.File newFileObj = browseDlg.getSelectedFile();
      if (newFileObj != null)
      {
         pathEntry.setText(newFileObj.getParent());
         fileEntry.setText(newFileObj.getName());
      }
   }
} // end showOpenDialog
End example

Figure 14.3 shows what this looks like using the default, native Java look and feel (Metal). To see this example, run the supplied sample TestJFileChooser, and click the List button.

Click To expand
Figure 14.3: The Swing Open/Save dialog (JFileChooser).

This example uses the JFileChooser dialog in Open mode because it calls the showOpen Dialog method, but you could also call the showSaveDialog method. Both put up the dialog modally, meaning it doesn't return until the user successfully closes the dialog. The difference between the two is the terminology of the title (Open versus Save) and the OK button (Open versus Save).

Unlike AWT's FileDialog, this dialog does not give a warning in Save mode if the selected or typed-in file already exists. You must test for this when you get control back. If you look at the code, you will see the method call returns a File object, not String objects for the path and the file name. This means you must use getParent to get the path of the selected file, and getName to get the name of the selected file. However, it makes other operations easy, such as testing for the selected file's existence. For this, just use the ts method on the returned File object. You can also test if the returned object is a file or a directory by using the isFile or isDirectory methods.

The example on the CD-ROM is called TestJFileChooserDialog, and it requires JDK 1.2.x, not 1.1.x using the Swing add-on. To use it with 1.1.x and the Swing add-on, change the imports from javax.swing to com.sun.java.swing. Table 14.3 lists some of the more interesting methods you can call on a JFileChooser object.

Table 14.3: Interesting JFileChooser Methods.
Method | Description
setApproveButtonMnemonic(char) setApproveButtonText(String) setApproveButtonToolTipText(String) | Tweak the appearance of the Open or Save button.
setDialogTitle(String) | Set a dialog-box title.
setFileSelectionMode(int) | Allow users to select files, directories, or both: FILES_ONLY, DIRECTORIES_ONLY, FILES_AND_DIRECTORIES
setCurrentDirectory(java.io.File) | Set the initial directory.
setSelectedFile(java.io.File) | Set the initial file selection.

Notice at the bottom of Figure 14.3 that the "Files of type" dropdown list contains on "All Files (*.*)." Unlike AWT's FileDialog, you can change this in Swing. You can add your own filters if you only want users to see files of a particular type.

The trick is to write a class that extends the FileFilter class in the javax.swing.filechooser package, and supply two methods in it: public boolean ac-cept(File f), which is called by Java for every file in the directory, and public String getDescription(), which is called by Java to get the text to put in the dropdown. In the accept method, return true if the given file matches your criteria for inclusion in the list. Listing 14.12 provides an example, which is used to subset the list to show only Java files.

Listing 14.12: A subset to show only Java files.
Start example
import javax.swing.*;
import javax.swing.filechooser.*;
import java.io.*;

public class JavaSwingFileFilter
            extends javax.swing.filechooser.FileFilter
{
   public boolean accept(File f)
   {
      if (f.isDirectory() ||
            f.getName().toLowerCase().endsWith(".java"))
         return true;
      else
         return false;
   }
   public String getDescription()
   {
      return "Java source files";
   }
} // end JavaSwingFileFilter
End example

Note that accept returns true in if the file is a directory or if it ends in .java. If you don't check for directories, they won't show up in the list, restricting users from being able to drill down through the directory tree. To use this, the code in Listing 14.11 should be changed to include these lines:

JavaSwingFileFilter javaFilter = new JavaSwingFileFilter();
browseDlg.addChoosableFileFilter(javaFilter);
browseDlg.setFileFilter(javaFilter);

These three lines added before the call to showOpenDialog will add the filter "Java source files" to the dropdown in the JFileChooser. When selected, users will only see directories (that they can double-click on to drill down) and files that end with the .java extension.

How Do I List Drives?

As of JDK 1.2.0, there is now a way to retrieve a list of drives on your current system. This is done using the listRoots static method in the File class. This method returns an array of File objects, as shown in Listing 14.13.

Listing 14.13: Retrieving a list of drives on your current system.
Start example
import java.io.*;

public class TestListDrives
{
   public static void main(String args[])
   {
      File drives[] = File.listRoots();
      for (int idx=0; idx < drives.length; idx++)
      System.out.println(drives[idx]);
   }
}
End example

This produced the following on our Windows NT system:

A:\
C:\
D:\
E:\

How Do I Create a Directory?

Once again, use the java.io.File class. The mkdir (make directory) method is the one you want. First, you must instantiate an instance of File that contains the fully qualified new directory name, as shown in Listing 14.14.

Listing 14.14: Creating a directory.
Start example
import java.io.File;
public class TestMkDir
{
   public static void main(String args[])
   {
      String currDir = System.getProperty("user.dir");
      String newDir = currDir + "\\newdir";
      File dirObject = new File(newDir);
      System.out.println("mkdir " + dirObject.getAbsolutePath());
      boolean ok = dirObject.mkdir();
      System.out.println("result was: " + ok);
   }
}
End example

This procedure leads to the following:

C:\JAVA\misc>java TestMkDir
   mkdir C:\JAVA\misc\newdir
   result was: true

How Do I Create or Open a Flat File and Write to It?

Contrary to what you might expect, flat files (such as those on your workstation drives) are not created using the java.io.File class. Instead, they are created by using the "zoo" of stream classes in the java.io package. This package contains numerous interfaces, classes, and class hierarchies for dealing with streams. (Streams are input or output devices that are read or written bytes at a time.) These classes are designed to be used in a nested fashion. They use one object to create, and another to get the desired input/output interface. Some examples are buffered or unbuffered, random or sequential, byte at a time or line at a time, and binary or text.

These are described in most Java books, but that level of detail is beyond the scope of this book. What you need to know for our purposes is that, to create a file, you typically use the FilterOutputStream class. This passes the File object that was created with the file path and intended file name into the constructor. If the file exists, it will be replaced. If you want to first check for the existence of a file, use the File object's exist method. If you intend to write to the file a line at a time, "nest" the FileOutputStream object inside a PrintWriter object. This enables access to that class's println (print line) method, as shown in Listing 14.15.

Listing 14.15: Writing lines of text to a flat (stream) file.
Start example
import java.io.*;

public class TestWriteFile
{
   public static void main(String args[])
   {
      File outFile =
         new File(System.getProperty("user.dir"),"myFile.tst");
      try
      {
         PrintWriter outFileStream =
         new PrintWriter(new FileOutputStream(outFile));
         outFileStream.println("test");
         outFileStream.flush();
         outFileStream.close();
      }
      catch (IOException exc)
      {
         System.out.println("Error opening file " +
                           outFile.getAbsolutePath());
         System.exit(1);
      }
      System.exit(0);
   } // end main method
} // end TestWriteFile class
End example

The next step is to write lines to this file. As you have done all along, with System.out, use the println method:

outFileStream.println("Hey, I get it now!");

Because PrintWriter is a "buffered" stream, println does not immediately write to disk. For performance reasons, that happens only when its internal buffer is full. If this causes a problem, specify true as a second parameter to its constructor, or explicitly call its flush method after every call to println.

Finally, you should make a habit of calling close when you are done with a file to unlock it and free its resources. Keep in mind that applets cannot, by default, read or write local files.

How Do I Read a (Flat) File?

For both writing to an output flat file and reading from an input flat file, use the plethora of classes in the java.io package. We assume that you want the file to be read efficiently (buffered) and to be read as text, not binary. If that is the case, you open an existing file by using a nested class approach as shown in Listing 14.16.

Listing 14.16: Reading a flat file.
Start example
import java.io.*;
public class TestReadFile
{
   public static void main(String args[])
   {
      File inFile =
         new File(System.getProperty("user.dir"),"myFile.tst");
      try
      {
         BufferedReader inFileStream =
            new BufferedReader(
               new InputStreamReader(
                  new FileInputStream(inFile) ) );
         boolean done = false;
         while (!done)
         {
            String line = inFileStream.readLine();
            if (line != null)
               System.out.println(line);
            else
               done = true;
         } // end while loop
         inFileStream.close();
      }
      catch (IOException exc)
      {
         System.out.println("Error reading "
                     + inFile.getAbsolutePath());
         System.exit(1);
      }
      System.exit(0);
   } // end main method
} // end class TestReadFile
End example

Notice that the readLine method was used to read the file a line at a time.

How Do I Call Another Program or Command?

While not heartily recommended for portable applications, there are times when you might deem it necessary to invoke a local program object (such as a local executable file) on your Windows computer (or call a COBOL program from server Java). JNI for calling a DLL entry point has already been discussed in this chapter, but sometimes you want to run an executable rather than calling a DLL function.

There is a way to do this in Java that uses the Runtime class in the java.lang package. This class represents your Java runtime system. To get an instance of this class, code Runtime.getRuntime(). This returns an object of type Runtime, appropriately enough. You will find a number of useful features in this class that allow you to perform functions such as tracing calls, querying available memory, and forcing the garbage collector to run. The exec method is also found in this class, which you can use to execute other programs or commands. There are actually a number of versions of exec, but typically you need only the one that takes one string, which contains the command and its parameters.

The exec method in Runtime returns an object of type Process for managing the running command. The command will be started as soon as you use the exec method, but there are methods in the returned Process object that you can use to wait for the command to finish running and to query its returned value. One example of such a method gets the return code the command specifies when it exits. Also, you will probably want to capture and query any messages it issues to standard output or standard error. The Process object has methods for retrieving these as well.

You can use this ready-made method to run a given command string. It runs the command and captures its standard output and standard error lines as Strings in a Vector object, which you can subsequently cycle through. Because this example also includes a main method, you can test it using our new method, as shown in Listing 14.17.

Listing 14.17: Running native executables from Java.
Start example
import java.util.Vector;
import java.io.*;
public class TestRunCmd
{
   Vector output = new Vector();
   public static void main(String args[])
   {
      TestRunCmd me = new TestRunCmd();
      if (me.runCmd("notepad"))
         me.processOutput();
   } // end main method
   // helper method to run an external command
   public boolean runCmd(String cmd)
   {
      boolean ok = true;
      System.out.println(cmd);
      Process process;
      try {
         process = Runtime.getRuntime().exec(cmd);
      }
      catch (IOException exc)
      {
         System.out.println("unexpected error running command: " + cmd);
         return false;
      }
      String line;
      // capture standard error
      DataInputStream err =
         new DataInputStream(process.getErrorStream());
      BufferedReader berr = new BufferedReader(
         new InputStreamReader(err));
      try {
         while ((line = berr.readLine()) != null)
         output.addElement(line);
      }
      catch(IOException exc) {}
      // capture standard output
      DataInputStream in = new DataInputStream(
         process.getInputStream());
      BufferedReader bin = new BufferedReader(
         new InputStreamReader(in));
      try {
         while ((line = bin.readLine()) != null)
            output.addElement(line);
         int rc = process.waitFor();
      }
      catch(Exception exc) {}
      return ok;
   } // end runCmd method
   public void processOutput()
   {
      if (!output.isEmpty())
      {
         String line;
         for (int idx = 0; idx < output.size(); idx++)
         {
            line = (String)output.elementAt(idx);
            System.out.println(line);
         }
         output.removeAllElements();
      } // end if !output.isEmpty
   } // end processOutput method
} // end class TestRunCmd
End example

The Windows Notepad window comes up when you run this example. After that window is closed, your Java program exits and returns control to the command line. Had the command been one that produced standard output (like the javac.exe command, for example), that output would have been captured and displayed.

How Do I Save and Remember User Profile Information?

In your programs, you might want to record user profile information, such as preferences. To do this in Java, use a class called Properties from the java.util package. The Properties class is a handy utility for storing name/value pairs, which Java refers to as properties. In other words, it is a table of Strings that can hold any information you want to put in it. Each String object is indexed in the table using another String value for the key. So, you might store information about a user, as shown in Table 14.4.

Table 14.4: An Example of Properties.
Name | Value
"LIBRARY" | "MYLIB"
"NUMBER" | "0011001"

These might represent, for example, the values previously entered into entry fields. That will allow you to default these entry fields with the same values on the next use of your application. To store this information, create an instance of the Properties class. Then use the setProperty method this class inherits from its parent class, java.util.Hashtable:

Properties profile = new Properties();
profile.setProperty("PATH", pathEntry.getText());
profile.setProperty("FILE", fileEntry.getText());

At this point, the information only exists in memory. To save it to a local flat file, use the save method for JDK 1.1.x, or the new store method for JDK 1.2.0 or higher (save has been deprecated, meaning it is no longer the preferred method). You specify a FileOutputStream object for the file to save it to, and a header String to be placed as the first line in the file for documentation, as in Listing 14.18. (Creating or replacing an output flat file has already been covered in this chapter.)

Listing 14.18: Saving a Profile object to disk.
Start example
static final String PREFERENCES_DIR = "c:\\";
static final String PREFERENCES_FILE = "preferences.dat";
File outFile = new File(PREFERENCES_DIR, PREFERENCES_FILE);
try {
   //profile.save(new FileOutputStream(outFile), "User Preferences"); // 1.1.x
   profile.store(new FileOutputStream(outFile), "User Preferences");  // 1.2.x
}
catch (Exception exc)
{
   System.out.println("Error saving profile info: " + exc.getMessage());
}
End example

You can be as creative as you want with the file name and extension. Some common extensions are .dat, .txt, .ini, .pro, and .prf, but the choice is yours. When saving to a local file, it is often safe to assume that there is only one user for that workstation. So, any file name will do. If you decide to store the files on a common server, you should probably name the file with the user's ID.

Now that you have this information saved, you need to be able to read it in again. That's quite easy. Define a Properties object and use its load method, specifying a FileInputStream as a parameter. This will populate the Properties object from disk, after which you can use the getProperty method to read individual values from it. An example is shown in Listing 14.19.

Listing 14.19: Reading a Profile object from disk.
Start example
oldValues = new Properties();
try
{
   File inFile = new File(PREFERENCES_DIR, PREFERENCES_FILE);
   oldValues.load(new FileInputStream(inFile));
   String oldPath = oldValues.getProperty("PATH");
   String oldFile = oldValues.getProperty("FILE");
   pathEntry.setText(oldPath);
   fileEntry.setText(oldFile);
}
catch (Exception exc){ } // no previous values
End example

When defining a Properties object, you can specify another Properties object in the constructor, for example:

new Properties(defaultProperties);

The values in the other object are used as defaults that "prime" the new object. This is a convenient way to specify default values for those entries that are not found in the file on disk.

To see the full example of using properties, both saving and restoring, refer to the TestProperties file on the CD-ROM for an example leveraging an AWT GUI, or TestPropertiesWithSwing for the same example leveraging a Swing (1.2.x) GUI. In both cases, use the Save button to save the current GUI contents to disk, and the Restore button to restore the previously saved contents.


Java Beans

Now that you have seen examples of some common programming situations in Java, let's turn to some common Java terms you might have heard. For example, can you define Java beans? If not, don't be afraid that you don't know beans! The fact is that Java beans are not very mysterious. They are simply "dressed up" Java classes. In other words, they are Java classes that follow a few conventions and, in some cases, add a little functionality to a typical class.

Java is all about reuse. The goal is to write Java classes that others can readily use, time and again. The conventions and optional added functionality that Java beans offer are designed to make general reuse of Java classes as easy as possible. Thus, it is likely that you will buy Java beans off-the-shelf instead of always building your own Java code from scratch. Want Java code to verify zip codes? Don't build, buy!

The conventions of Java beans make it easier for Java application development tools to "query" the methods and functions they offer. What's more, the Java beans standard is designed to allow classes with no prior knowledge of each other to cooperate. For example, a database access bean can be used to populate a chart bean.

Think of Java beans as the Java equivalent to Microsoft VBX, OCX, or ActiveX technologies. All of them are designed to foster a rich third-party market in off-the-shelf functionality that can be easily purchased or downloaded, and added to a GUI Builder's palette of existing parts.

To create such an environment, it is necessary to define some standards that third-party developers follow, so that tools can be written to connect one arbitrary part to another arbitrary part, without previous knowledge of each other. For example, a GUI Builder tool should be able to determine easily what the public methods and instance variables are inside a given class, and display them to you. Furthermore, to allow the class to be shown as an icon in the GUI Builder palette, and then dragged and dropped onto the canvas (which instantiates an instance of that class), the class must have a default constructor, since that is what the GUI Builder will generate code to call.

The optional functionality, beyond public methods, variables, and a default constructor, refers to the ability of one Java bean to "listen" for changes made to another. Imagine you have an entry-field bean in your user interface, and you want whatever the user types into it to drive a selection in a list-box bean. If a is typed, the first entry that starts with a is selected; if ab is typed, the first entry that starts with ab is selected; and so on.

Alternatively, imagine that you have a push button labeled "Connect" that connects to OS/390 or OS/400. You want that button to be disabled when there is a live connection, and enabled when there is not. That is, you want the enabled state of the button to be tied to the connected state of another OS/390 or OS/400 connection-class object. If these classes exploit the optional functionality of beans, they can do this.

These types of dependencies are very common. Java beans define a way to tie one class to another in these situations. You can always code these by hand using traditional means, but the power of Java beans is that you can make these connections between two Java classes without prior intimate knowledge of either.

This is most important for tools that allow you to graphically "script together" independent Java classes. These tools need a common component interface that all Java classes they work with adhere to. This commonality enables you to use Java classes from anyone and yet have the tool work with those classes as though the tool had intimate knowledge about them. For example, the tool will let you call a selected method in one bean when a selected property in another bean changes. All Java tools support Java beans, including IBM's VisualAge for Java.

Java Bean Features

The key thing is that the Java bean standard for writing classes facilitates a world in which anyone can write and sell Java classes-without having to supply the source code-and all tools in the Java application development business can work effectively with those classes. Effectively, in this case, means that you can place those classes in the tool's graphical Builder palette and connect features of one class to features of another. For example, you can configure a push button "pressed event" of a button bean to invoke a "print" method of a report bean. It also means that there must be a well-defined way for these tools to discover the features of a Java class so they can be presented to the programmer in an easy-to-select and easy-to-use way. To do this, the Java bean specification defines features that the Java classes that adhere to it may contain. These are shown in Table 14.5.

Table 14.5: Features of a Java Bean.

Feature

Description

Properties

There's nothing new here; these are simply instance variables in the class that other beans can read or write. As per good practice, the variables themselves are defined as private/protected, and getXXX and setXXX methods are defined for reading and writing them (or isXXX, if the property is Boolean). Java assumes each getXXX and isXXX method represents a readable property called XXX, and each setXXX method represents a writable property called XXX. If you follow the get/set convention for variable access, you are following the Java bean convention.

Bound Properties

These are new. They are the same as a property, in that users can read and optionally write them, but users can also track the changes in the variable's state. These require some additional programming to enable.

Methods

There's nothing new here; these are just your public methods in the class-that is, these are the methods others are allowed to call.

Events

These are new. They are triggers that other beans can monitor for, other than a variable state change. They are typically used in user interface classes, and correspond to something a user has done.

For example, Java defines events in its java.awt.event and javax.swing.event packages corresponding to pushing a push button, double-clicking on a list entry, selecting a radio button, and so on. You may define an event to be anything, such as inventory falling below a certain threshold. Other classes can monitor for events, just as they can for bound property changes. For example, you can have a method in your class called when a push button in a Button object is pushed. When events happen, they are said to be fired. Supporting events requires additional programming on your part.

Java Bean Introspection

Tools that support Java beans typically allow you to see all the features any selected bean supplies. How do the tools do this? They use a built-in Java technique called introspection. Keep in mind that you create Java bean features in your classes by using well-defined standards or classes in the java.beans package. This means that Java can query your class and identify all the properties, methods, and events it supports. This query is referred to as introspection. For example, all getXXX methods found are assumed to represent readable properties called "XXX," and all setXXX methods found are assumed to represent writable properties called "XXX." All public methods are assumed to represent methods that should be exposed for anyone to call.

This default behavior can be tweaked by supplying a Bean Info class that explicitly lists these if you don't like the default behavior of introspection. Java tools use it to extract this list for the user who wants to work with your Java bean. Choosing names for your features that are meaningful and self-describing will make your Java bean much easier to use.

Thinking Java Beans

Before you begin to design a Java bean class, take some time to consider the needs of the programmer users of your class. Those unknown programmers will try to use your class without the benefit of your source code. Your thought process might following the steps in Table 14.6.

Table 14.6: Java Bean Design Thought Process.
Thought | Description
What variables do I want to allow users to read? What variables do I want to allow users to write? | Define getXXX methods for those variables you want to be readable. Define setXXX methods for those variables you want to be writable. Note that the actual value returned on getXXX need not be a simple private variable; it could be a computed value. Tools determine the name of a property from the XXX string of the getXXX/setXXX methods, not from the variable name.
What variables do I want to enable users to track the state of? Is it enough to be able to query their state, or do users need to be able to be identified when their state changes? These are bound properties. | In the setXXX method of these trackable properties, code in a call to the firePropertyChange method of the java.beans.PropertyChangeSuppport class.
What methods do I want to expose for use by the public? | By default, all public methods in a class are exposed through Java introspection, but by supplying a java.beans.BeanInfo class, you can identify a subset of these methods to be exposed.
What events do I want to support? Is there anything that can happen during the life of the class that might be important to others? | If you identify user-initiated or other trigger events that are publicly important, in your code that processes this event, you will use the same event and event listener framework described for Java GUI components. The classes in java.awt, for example, are Java beans, and they do fire events already, as described in chapter 12.

Java Bean Example

The procedure for creating beans is beyond the scope of this book. What's more, there is no concept in COBOL that is remotely similar.

A brief introduction to what's involved might be useful, though. We used the wizards in the VisualAge for Java product to create a simple Inventory bean that has the features listing in Table 14.7.

Table 14.7: The Features of the Inventory Java Bean.
Feature | Description
inStock bound property | This is just an integer representing the number of items in stock. It is bound so users can track its value.
actionPerformed event | So we did not have to define a new event type, we reused the java.awt.event.ActionEvent class and put in code to fire an event of this type when the inStock value dips below ten.

Figure 14.4 shows what the BeanInfo page of VisualAge for Java looks like for this bean. Notice the use of the superscripts P and E in the Features list to distinguish Properties from Events. Notice also that the source for the setInStock method is currently displayed. After this code was generated by VisualAge, we changed the line of code "fieldInStock = inStock" to be "fieldInStock += inStock." (That is, we changed "=" to "+=".) As a result, the changes are incremental to the existing stock value, instead of absolute. We also added the lines of code at the end, which will fire the actionPerformed event if the value of fieldInStock is below ten.

Click To expand
Figure 14.4: Using VisualAge for Java's BeanInfo page to create bean features.

To show how easy it is to use Java beans in a Java-bean-aware tool, we then used the Visual Composition Editor in VisualAge to construct a simple little test of this bean. The test frame window initializes the inStock property to 12, then ties two push buttons to the inStock property: Increment and Decrement. The former passes a parameter of 1; the latter passes a parameter of -1.

We also did a property-to-property connection of a read-only TextField (entry field) to our Inventory bean's inStock property, so that its value would always be reflected in this TextField. Finally, we connected the actionPerformed event of our bean to the text property of a Label bean at the bottom of the window, and specified the text to be "LOW STOCK!" Figure 14.5 shows the little test window, with the Visual Composition Editor shown in the background. (You can import the supplied repository learn.dat to see this for yourself in VisualAge.)

Click To expand
Figure 14.5: Testing a Java bean in VisualAge for Java's Visual Composition Editor.

Figure 14.6 shows what the Inventory class looks like in the Workbench window of VisualAge. Notice that we used the BeanInfo wizards to generate this code. Also note the use of the modifier transient, which VisualAge generated for us. This modifier, you recall, denotes a variable whose data is not significant enough to save when the Java object is serialized to disk.

Click To expand
Figure 14.6: VisualAge for Java's Workbench window.

Java Bean Grinds

Customization and indexed properties are only two of the multitude of topics on the subject of Java beans that we will leave untouched here. Our purpose is not to teach you all the details of how to code Java beans. Not only are there a lot of books and documentation on the subject, but tools such as VisualAge for Java are very helpful. What is most important is that you know how to use Java beans.

You can assume that any third-party Java code you buy will come in the form of Java beans. Java beans are, after all, effectively supersets of typical Java classes, and they offer the most robust reuse framework.

It is also important to note that a typical Java class that you write will use get/set methods and public methods, so (assuming it has a default constructor) it will already be a Java bean! It is only for defining bound properties, events, or customizers that it is necessary to delve deeper into the world of defining Java beans.

For more information, open the file c:\jdk1.2.2\docs\guide\beans\index.html, where c:\jdk1.2.2 is the directory where you installed the JDK. In addition, the following Java bean Web site contains everything you need to know about creating, using, and finding beans: http://java.sun.com/beans/. Other good sites for finding existing beans are www.gamelan.com, and VisualAge for Java's own site, www.ibm.com/software/ad/vajava . From this site, you can follow the link to IBM's VisualAge Developer Domain, where you can find many third-party Java beans for sale. Also, there are many Java beans at IBM's www.ibm.com/alphaworks site, under the title "alphabeans."


Websphere: Of Servlets, Javaserver Pages, Enterprise Java Beans, And More

The world of Java is exploding at an unprecedented rate. You have been given a very brief tour of the core Java language as of JDK 1.1.x and JDK 1.2.X. There is much more for you to discover now that your are familiar with the basics of Java. This includes Java Servlets, which are similar to Java applets but run on the server instead of the client. Rather than being embedded in the HTML, a Servlet's job is to output the HTML that will be displayed in a browser. Servlets work with your Web server software, and effectively allow your Web clients to invoke Java classes via the Web server, just as you can call COBOL and C programs on your server using CGI (Common Gateway Interface).

Servlets render CGI obsolete, and a number of industry initiatives that exist only to improve the CGI performance by offering APIs to run programs in the same address space as the Web server. Servlets offer the benefits of Java (safe, secure, OO, easy to program, and portable). Compared to CGI, they are faster because they are only loaded into memory once. OS/390 and OS/400 Web servers, both from IBM and others, support Servlets today. IBM offers Servlet support on many platforms in its WebSphere Application Server product, in all editions (Standard, Advanced, and Enterprise). The Standard edition is available for free on OS/390 and OS/400 operating systems. See www.ibm.com/software/webservers for more information.

Servlets

A Servlet is a Java class. Specifically, it is a Java class that extends the HttpServlet class that is part of the javax.servlet.http package (in most cases). This package is not part of the base JDK, but rather one of the standard extensions to the JDK that you can download from www.java.sun.com/products. For Servlets, you can download the Java Servlet Web Development Kit (JSWDK) which includes this package and the javax.servlet package it also needs, as well as some documentation, samples, and a little mini-HTTP server for testing purposes. Alternatively, these packages are shipped as part of the IBM WebSphere Application Server product on many platforms, and they are shipped with VisualAge for Java.

Servlets are used to dynamically create HTML on the fly. They are loaded into memory by the Web server software, and they are called by the Web server when a request for them comes in. Their job is to create a new page of HTML text, which they return to the Web server, which in turn sends it to the Web browser client, where it is displayed. They are typically invoked from a Web browser by use of HTML forms, which specify the Servlet on the server to run when the user presses for the Submit button on a Web page (just like CGI). Alternatively, the user could enter a Servlet URL directly, or via a link.

A Servlet URL, by default, contains the path /servlet/ in it, but you can configure the Web server to reroute any file name to a Servlet. The Web server software will load the Servlet class into memory the first time its called (or at startup, if configured for that), and leave it there for a configurable amount of time. Throughout the Servlet's lifetime, the Web server will call specific methods in it, much like a Web browser does for applets. The typical methods are shown in Table 14.8.

Table 14.8: Important Servlet Methods (Extending the javax.servlet.http.HttpServlet).
Method (all public) | Description
void init (ServletConfig config) | This is called once, when a Servlet class is loaded into memory. This is a good place to open database connections and initialize global variables.
String getServletInfo() | This method allows you to return a string describing the Servlet. Some Servlet administration tools, like WebSphere, will call this when displaying registered Servlets.
void doGet (HttpServletRequest req, HttpServletResponse res) | This method is invoked on an HTTP GET operation, which is usually the result of a user pressing the Submit button in an HTML form.
void doPost (HttpServletRequest req, HttpServletResponse res) | This method is invoked on an HTTP POST operation, which is usually the result of a user pressing the Submit button in an HTML form. POST is very similar to GET, but allows more parameters.Usually doPost just calls doGet.
void destroy() | This is called once, when the Servlet class is removed from memory. It is a good place to close database connections and other resources.

The first method the Web server will call is init, but it will only call that once when the Servlet is brought into memory. The method called for each client request is either doGet or doPost, depending on how the HTML form is coded (and you will usually design one of these to just call the other). In this method you do all your processing, including leveraging other Java classes, accessing the database, or calling existing applications and transactions.

The ultimate goal of your Servlet code is always to build a new page of HTML text, which when your method ends, will be sent back to the Web browser and become the next page shown there. This might be a page showing a customer's account information, a list of products and prices, or a menu of options. The key is that you can build the page on the fly using data you obtain from any source necessary.

Your Servlet doGet/doPost method is given two objects as parameters. The first contains information about the client, including the make and release of the Web browser and the user's session information. The second is for you to write your HTML lines of source to, using println method calls (but you have to call getWriter first to retrieve the PrintWriter object that you write your HTML to). It is time to have a look at a Servlet. Listing 14.20 shows a Servlet that returns an HTML page listing the current directory of the Web server and the files in it (using the JDK-supplied File class you saw earlier in this chapter).

Listing 14.20: A Servlet class that returns a Web page listing the current directory's contents.
Start example
import java.io.*;            // for PrintWriter
import javax.servlet.*;      // for servlet classes and interfaces
import javax.servlet.http.*; // for HTTP servlet classes and interfaces

public class TestAServlet extends HttpServlet
{
   private static final String AUTHOR = "Phil Coulthard"; // who wrote this?

   public void doPost(HttpServletRequest  request,
                     HttpServletResponse response)
               throws ServletException, IOException
   {
      PrintWriter out;                       // output stream
      String title = "TestAServlet";

      response.setContentType("text/html");  // set mime type to html
      out = response.getWriter();            // stream for writing html to
      out.println("<HTML><HEAD>");           // write html page and header
beginning
      out.println("<TITLE>" + title + "</TITLE>"); // write title tags
      out.println("</HEAD><BODY>");          // write end head, start body tags
      out.println("<H1>" + title + "</H1>"); // write heading tags
      DateFormat  dateFormatter = DateFormat.getDateInstance(DateFormat.SHORT);
      String dateString = dateFormatter.format(new Date());
      out.println("<P ALIGN=\"RIGHT\">Date: "+dateString+"</I></P>"); //date
      out.println("<HR>");                   // write horizontal rule tag

      String currDir = System.getProperty("user.dir");
      out.println("<p>Current Directory: " + currDir + "</p>");
      out.println("<p>Contents:</p>");
      out.println("<ul>");
      File currDirFile = new File(currDir);
      String files[] = currDirFile.list();
      for (int idx=0; idx<files.length; idx++)
         out.println("<li>  " + files[idx] + "</li>");
      out.println("</ul>");
      out.println("</BODY></HTML>");          // write end body, end page tags
      out.close();                            // all done with writer
   } // end of doPost method

   public void doGet(HttpServletRequest  request,
   HttpServletResponse response)
   throws ServletException, IOException
   {
      doPost(request, response);
   } // end doGet method

   public String getServletInfo()
   {
      String info = "Servlet: TestAServlet, Author: "+AUTHOR+", Release: 1.0";
      return info;
   }
} // end of class TestAServlet
End example

Notice, in the doPost method, the use of response.getWriter to get the object to write the HTML to. Also notice the call to setContentType on the response object, which tells the Web browser that HTML source is being sent. This is a simple Servlet, in that it does not process any user-supplied input via HTML forms. It was compiled with javac, and the class file was placed in the \WebSphere\AppServer\Servlets directory of IBM WebSphere Application Server for Windows NT (our local system). From there, it can be called from a Web browser by entering the URL http://localhost/servlet/TestAServlet, which results in a call to the doGet method, and ultimately the result in Figure 14.7.

Click To expand
Figure 14.7: An HTML page generated by the Servlet in Listing 14.20.

You probably won't need to list the files in your directory over the Internet. The point you should take from this example, though, is that your Servlet code can do whatever it needs to, including using JDBC calls, to get the data it requires to build up its output page. Remember that Servlets run on the server, so they have efficient access to everything there.

Let's see next how to use a Servlet to process input from an HTML form. Listing 14.21 shows the very simple HTML form.

Listing 14.21: The HTML source for a form.
Start example
<HTML>
<HEAD>
<TITLE>A Simple Input Form</TITLE>
</HEAD>

<BODY>

<FORM action="http://localhost/servlet/ProcessInputServlet"
method="post">
<LABEL for="name">Enter your name:</LABEL>
<INPUT type="text" name="name">
<LABEL for="age">Enter your age:</LABEL>
<INPUT type="text" name="age">
<INPUT type="radio" name="sex" value="M">Male</LABEL>
<INPUT type="radio" name="sex" value="F">Female</LABEL>
<INPUT type="submit" value="Register">
<INPUT type="reset"  value="Reset">
</P>
</FORM>

</BODY>
</HTML>
End example

This form, when opened in a Web browser and filled out, looks like Figure 14.8. (For more information on writing HTML forms, see http://www.w3.org/TR/REC-html40/ . This is the reference for HTML 4.0; just scroll down and click on chapter 17.)

Click To expand
Figure 14.8: An HTML input form.

In the example, notice the entry field "variables" in the form are named "name" and "age," while both radio buttons are named "sex." Also notice that the <FORM> tag specifies a Servlet to call, in this case, ProcessInputServlet. This Servlet is called when the user presses the Submit button for this form. The Reset button is a predefined button in HTML, which resets the entry fields to their original values. Listing 14.22 shows the code for the Servlet that processes this form.

Listing 14.22: A Servlet class to process an HTML form.
Start example
import java.io.*;            // for PrintWriter
import javax.servlet.*;      // for servlet classes and interfaces
import javax.servlet.http.*; // for HTTP servlet classes and interfaces

public class ProcessInputServlet extends HttpServlet
{
   private static final String AUTHOR = "Phil Coulthard"; // who wrote this?

   public void doPost(HttpServletRequest  request,
                     HttpServletResponse response)
               throws ServletException, IOException
   {
      PrintWriter out;                             // output stream
      String title = "ProcessInputServlet";

      // get the input...
      String inName = request.getParameter("name");
      String inAge  = request.getParameter("age");
      String inSex  = request.getParameter("sex");

      // write the output...
      response.setContentType("text/html");        // set mime type to html
      out = response.getWriter();                  // stream for writing html to
      out.println("<HTML><HEAD>");                 // write page, header beginning
      out.println("<TITLE>" + title + "</TITLE>"); // write title tags
      out.println("</HEAD><BODY>");                // write end head, strt body tags
      out.println("<H1>" + title + "</H1>");       // write heading tags
      out.println("<HR>");                         // write horizontal rule tag

      out.println("<p>Your input was:</p>");
      out.println("<ul>");
      out.println("<li>Name = '" + inName + "'");
      out.println("<li>Age  = '" + inAge  + "'");
      out.println("<li>Sex  = '" + inSex  + "'");
      out.println("</ul>");

      out.println("</BODY></HTML>");               // write end body, end page tags
      out.close();                                 // all done with writer
   } // end of doPost method

   public void doGet(HttpServletRequest  request,
                     HttpServletResponse response)
               throws ServletException, IOException
   {
      doPost(request, response);
   } // end doGet method

   public String getServletInfo()
   {
      String info =
         "Servlet: ProcessInputServlet, Author: "+AUTHOR+", Release: 1.0";
      return info;
   }
} // end of class ProcessInputServlet
End example

The output of this Servlet, which is returned after the user presses the Register button on the form, is shown in Figure 14.9. To process the input fields from the HTML form, the Servlet use the getParameter method call on the given request object, and identifies the input field to query by giving its name (coded with name=xxx in the form). It gets back the String contents of that field, or if the field doesn't exist in the input form, it gets null. After that, it prepares an output HTML page, and in this case, just echoes the input to show that it works.

Click To expand
Figure 14.9: The output of the ProcessInputServlet Servlet.

You can imagine, then, that you can ask for enough information to identify and qualify a user, then query the database or call an existing application or transaction to get information for him or her. This information is subsequently written to the returned page and ultimately displayed to the user.

A new and nice complement to raw HTML as you see it here is JavaScript or its standardized variant, ECMAScript, which is a loosely typed language that resembles Java is some ways. You can embed JavaScript right in your HTML and use it to write functions that are tied to events, such as a button click. The key thing is that JavaScript is executed on the client in the Web browser. It can't do any server-side accessing, but it is a great way to do verification of input, for example, before submitting the input to a Servlet on the server. We don't cover JavaScript or ECMAScript here, but you can find many books on them at your favorite online bookstore.

JavaServer Pages

JavaServer Pages (JSPs) are simply a better way to write Servlets. Specifically, a JSP is a file on the server that ends in .jsp, and contains HTML tags, embedded Java code, and some other unique tags designed specifically for JSPs. If you consider the Servlet code you've seen here, you might agree that one down side to it is that it is outputting HTML tags right inside the Servlet.

What if you want to give the HTML to a person skilled in HTML design to "fix up"? You would have to give this non-programmer your Java code and hope he or she can figure it out and make the changes, without breaking the code. Then you would need to get it back and compile it. This mix of viewing and coding means you would have to mix your skills, as well. Even if you are a good HTML writer, you might still like to use one of the many HTML WYSIWYG editors, such as TopPage (which comes with WebSphere Studio, the tooling half of WebSphere). Servlets, however, don't allow for this.

This was the rationale behind JavaServer Pages, which are an industry de facto standard now, and at least at the time of this writing, are still driven by Sun Microsystems. JSPs reverse the idea of Servlets. That is, you can think of a Servlet as being Java code that contains HTML source inside it. Think of a JSP as an HTML file with Java source inside it. Instead of calling a Servlet directly, users instead can type in the name of a JSP file, like http://www.myserver.com/myPage.jsp, and the Web server software will do the following:

  1. Find the myPage.jsp file on the server's disk

  2. Convert it to a server where the embedded Java code becomes the doGet body, and the HTML source gets wrapped with out.println(…) statements.

  3. Compile the generated Servlet with javac.

  4. Load and run the Servlet.

Of course, this could all be quite slow, so steps 1, 2, and 3 are actually only done once. After that, only step 4 is done, unless the .jsp file subsequently changes. The power of this is that the Java code is embedded using special tags that tools ignore, so you can use WYSIWYG tools to finesse the HTML look and feel, and then use a source editor to edit the embedded Java. Note that WYSIWYG HTML tools like Page Designer in IBM's WebSphere Studio are now stepping up to supporting the direct editing of the embedded Java, too.

Let's use an example to bring this home. We will redesign the first Servlet, TestAServlet in Listing 14.20, to be a JSP instead. Some details are necessary first, though. To embed the Java code inside your HTML source, surround it with <% and %>. If you have a variable or expression whose value you want included directly in place in the HTML, surround it with <%= and %>.

Java code is not very useful without variables, and there is support to define Java-bean class objects as variables, which you will see shortly. The most important variables you need are predefined for you, namely, request, response, out, and in. These are, respectively, an HttpServletRequest Servlet parameter, an HttpServletResponse output parameter, a PrintWriter object for doing println, and a streamed input reader of type BufferedReader. There are also special tags or directives that start with <%@ and end with %> for affecting the generated Java code. One of these is

<%@ imports = "xxx" %>

It generates import statements at the top of the Servlet, which are needed in the example for java.io.*, java.util.* and java.text.*. Other directives include content_type, extends, and implements. In addition, since the generated Servlet will, by default, only contain doGet and doPost methods, you can include instance variables and constants, and other methods, by using JSP declaration syntax. This starts with the tag <SCRIPT RUNAT="server"> and ends with </SCRIPT>. Whatever you put in-between ends up inside the class of the generated Servlet. The example uses this to generate the class-level constant and the getServletInfo method. Finally then, the JSP for the example is shown in Listing 14.23.

Listing 14.23: A JSP that displays a Web page listing the current directory's contents.
Start example
<HTML><HEAD>
<SCRIPT RUNAT="server">
private static final String title = "TestAJSP";
</SCRIPT>
<TITLE><%=title%></TITLE>
</HEAD><BODY>
<H1><%=title%></H1>

<%@ import = "java.io.*" %>
<%@ import = "java.util.*" %>
<%@ import = "java.text.*" %>

<SCRIPT RUNAT="server">
private static final String AUTHOR = "Phil Coulthard"; // who wrote
this?
</SCRIPT>
<% DateFormat  dateFormatter =
   DateFormat.getDateInstance(DateFormat.SHORT);
   String dateString = dateFormatter.format(new Date()); %>
<P ALIGN="RIGHT">Date: <%= dateString %> </I></P>
<HR>
<%   String currDir = System.getProperty("user.dir");%>
<p>Current Directory: <%= currDir %></p>
<p>Contents:</p>
<ul>
<%   File currDirFile = new File(currDir);
     String files[] = currDirFile.list();
     for (int idx=0; idx<files.length; idx++)
     {
%>
      <li>   <%= files[idx] %> </li>
<%    } %>
</ul>

</BODY>

<SCRIPT RUNAT="server">
    public String getServletInfo()
    {
       String info = "Servlet: TestAServlet, Author: "+AUTHOR+",
                      Release: 1.0";
       return info;
    }
</SCRIPT>
</HTML>
End example

You can see that the JSP inverts the Servlet, so that the static HTML tags are the main part of the file. Embedded among those are JSP tags for including Java code. When you run this the first time (by entering http://localhost/TestAJSP.jsp), it takes a few seconds for the initial compile step. Then, you should see the same output as for the Servlet (Figure 14.9), except with the changed title ("TestAJSP") coded in Listing 14.23.

In order for your IBM HTTP Server to find the .jsp file, it must be in the default directory used for finding .html files. By default, this is c:\IBM HTTP Server\htdocs, where c: is the directory on which the HTTP Server was installed. Remember, these examples were run on Windows NT using downloaded trial versions of IBM's WebSphere Application Server (2.02) and IBM's HTTP Server (www.ibm.com/software/webservers). If you are attempting this yourself, after installing, be sure to open your Control Panel, double-click the Services icon, and configure the IBM HTTP Server there as shown in Figure 14.10. Note also that the password was set to httpd.

Click To expand
Figure 14.10: Configuring the IBM HTTP Server service.

As mentioned earlier, JSPs have the capability to leverage Java beans. This is done using the <BEAN> tag, where you identify the class name of the bean and some additional attributes, such as whether to instantiate it now or assume it is already instantiated. The only "beany" thing important in these classes is that they have a default constructor, and that they have get and optionally set methods for retrieving and optionally setting values.

Once you "declare" a bean in a JSP, you can use the <%= syntax to embed expressions like myBean.getMyVariable(), which will generate the retrieved value into the HTML. This has the capability of making your JSPs significantly less cluttered with Java code, since you can put the code into a Java class and then use this simple syntax to retrieve the results. This makes for much cleaner JSPs that are easier to understand by HTML gurus who don't like looking at Java code.

Let's see this in action. All the Java code used to get the current date, directory, and directory contents will be extracted from the example and placed in a separate class. Then, the JSP will be modified to just use that Java class. First, Listing 14.24 shows the necessary Java class.

Listing 14.24: A Java class to return the current directory's contents.
Start example
import java.io.*;            // for File
import java.util.*;          // for Date
import java.text.*;          // for DateFormat

/** A Java Bean for supplying data to a JSP */
public class DirInfo
{
   private String currDir;
   private String files[];

   /**
   * Constructor. Initializes instance variables
   */ public DirInfo()
   {
      currDir = System.getProperty("user.dir");
      File currDirFile = new File(currDir);
      files = currDirFile.list();
   }
   /**
   * Getter to retrieve current date
   */ public String getDate()
   {
      DateFormat  dateFormatter =
         DateFormat.getDateInstance(DateFormat.SHORT);
      return dateFormatter.format(new Date());
   }
   /**
   * Getter to retrieve current directory name
   */
   public String getCurrDir()
   {
      return currDir;
   }
   /**
   * Getter to retrieve number of files in current directory
   */
   public int getNbrFiles()
   {
      return files.length;
   }
   /**
   * Getter to retrieve i'th file name in current directory
   */
   public String getFile(int index)
   {
      return files[index];
   }
} // end of class DirInfo
End example

Next, the JSP is changed to use this bean instead of having all the Java code itself. Note the use of the <REPEAT> tag, which is easier than an embedded Java for loop, except it forces you to hard-code the ending limit, which we will set to show just the first 20 files. The <REPEAT> tag is an IBM extension to JSPs that is supported by IBM's WebSphere Application Server product. The new, smaller JSP is shown in Listing 14.25. Note that to do this, you must (by default) place your class file in the servlets subdirectory.

Listing 14.25: A simplified JavaServer Page that uses a Java class to do the logic.
Start example
<BEAN NAME="dirinfo" TYPE="DirInfo" INTROSPECT="no" CREATE="yes"
      SCOPE="request">
</BEAN>
<HTML><HEAD>
<SCRIPT RUNAT="server">
private static final String title = "TestAJSP";
</SCRIPT>
<TITLE><%=title%></TITLE>
</HEAD><BODY>
<H1><%=title%></H1>

<%@ import = "java.io.*" %>
<%@ import = "java.util.*" %>
<%@ import = "java.text.*" %>

<SCRIPT RUNAT="server">
private static final String AUTHOR = "Phil Coulthard"; // who wrote
this?
</SCRIPT>

<P ALIGN="RIGHT">Date: <%= dirinfo.getDate() %> </I></P>
<HR>
<p>Current Directory: <%= dirinfo.getCurrDir() %></p>
<p>Contents:</p>
<ul>
<REPEAT INDEX=idx START=0 END=20>
<li> <%= dirinfo.getFile(idx) %> </li>
</REPEAT>
</ul>

</BODY>

<SCRIPT RUNAT="server">
   public String getServletInfo()
   {
      String info = "Servlet: TestAServlet, Author: "+AUTHOR+",
Release: 1.0";
      return info;
   }
</SCRIPT>
</HTML>
End example

What you have seen so far is a JSP designed to be called directly by the user. Another option, used more often, is to design your site so that a Servlet is called directly, and then design the Servlet to call a JSP as its last step. This is done using the following lines of code:

// 1) Set the bean as an attribute in the current request object
((com.sun.server.http.HttpServiceRequest) request).setAttribute(
          "dirinfo", dirinfo);
// 2) Use callPage to invoke JSP file and pass the current request object
((com.sun.server.http.HttpServiceResponse) response).callPage(
          "/TestJSP3.jsp", request);

The callPage method takes a JSP file name and the request parameter to your method as parameters to it. It forwards control to the JSP. Note that control does not return to the Servlet. The idea here is that your Servlet will instantiate and populate a Java bean (perhaps by using JDBC to call a database). It will then call a JSP to display the results of that bean.

To do this, you have to change the bean tag in your JSP to indicate CREATE=no and SCOPE=request. Then, by calling setAttribute in your Servlet, you make the bean available to the JSP. Alternatively, you could specify SCOPE=session, and put the bean into the session object. The session object is unique per user. You get it by calling:

HttpSession session = request.getSession(true);

Once you have it, you can put beans into it like this:

session.putValue("MYBEANNAME", beanObj);

For frequently referenced beans, this can be more efficient because you only need to place it there once. In either case, the name given for the first parameter must match the name you specify on the NAME parameter of the <BEAN> tag in your JSP. You can see the example Servlet reworked to instantiate a DirInfo bean and call a JSP on the supplied CD-ROM. The Servlet file is TestServletJSP.java and the JSP file is TestAJSP3.jsp.

Note

This discussion pertains to the JSP specification 0.91 or 0.92, which is what WebSphere Application Server 2.x supports. The JSP specification at the time of this writing was just updated to 1.0, and WebSphere Application Server release 3.0 was just released on Windows with JSP 1.0 support. The <BEAN> tag changed to <jsp:useBean>, and the callPage method changed to dispatch in a different class. You can find a JSP 1.0-compliant version of this sample in the files TestServletJSP1.java and TestAJSP10.jsp on the CD-ROM.

So, you finally have a very nice architecture that cleanly separates the code (Servlet) from the display (JSP). HTML gurus can simply tell you that they need a bean that contains customer information, and you can write the Servlet that instantiates and populates the bean, and then calls the JSP written by the HTML gurus.

We recommend that the Servlet not contain too much "business logic" itself, but rather defer that to another class that is not a Servlet but is called by the Servlet. By some conventions, this class is called a command bean and has a method called execute to do the work (say, calling a database, a CICS transaction, or a COBOL program). It returns the populated bean object needed by the JSP. If you need more than one bean in your JSP, your Servlet might use more than one command bean to get them. So, the Servlet's job becomes one of calling the necessary command beans to get the data, then calling the appropriate JSP to display the data-or, if anything goes wrong, calling an error JSP to display an error message.

JSPs and Servlets are fun to work with, and they are a great way to spruce up your user interface and extend your reach onto the Web. We leave this section by mentioning that IBM's VisualAge for Java product, combined with WebSphere Studio for the HTML/JSP side of things, is a great tool for JSP and Servlet development. For example, within VisualAge for Java, you can debug a JSP using its JSP Execution Monitor. As you single-step through it, you will see, simultaneously, the JSP source, the generated Servlet Java source, and the outputted HTML. Way cool!

WebSphere Studio has a SmartGuide that, given any Java bean, will generate two JSPs and a Servlet from it. One JSP prompts the user for the input data, the other displays the output data, and the Servlet controls them. The Servlet gets control from the input JSP, instantiates the bean and sets the input properties from the JSP, calls the selected method in the bean to populate it, and finally calls the output JSP to display the output properties from the bean. You can just use the HTML WYSIWYG editor to spice up the generated JSPs.

Enterpise Java Beans (EJBs)

Enterprise Java Beans are another interesting initiative that applies to you as a server programmer. These are "grown-up" Java beans that support enterprise functionality, such as message queuing, naming, transactions, and CORBA's IIOP networking protocol. While Java beans are mainly used on the client, EJBs are strictly for the server. To run them, you must get an "Enterprise Java Bean shell" or server that can handle all the ugly but necessary infrastructure, such as naming, lookup, persistence, security, and transactions.

IBM supports EJBs in its WebSphere Application Server product (Advanced and Enterprise Editions). Both editions have EJS, Enterprise Java Server, IBM's EJB shell. WebSphere Application Server is available on both OS/390 (Enterprise) and OS/400 (Advanced), as well as numerous other operating systems. WebSphere Enterprise Edition is a superset of Advanced Edition that adds support for other languages like C and C++, and the notion of managed objects. There are also many third-party vendors offering alternative shells. All conform to the shell specification from Sun, which was developed with input from IBM and others who have deep heritages in this field.

Enterprise Java Beans run inside containers. The containers all live inside the EJ shell. EJBs can be accessed by "client" code that either runs on the same server, a different server, or a client such as Windows or a Web browser. The most popular model is to access EJBs from Servlets, which use the results to build Web pages that are then sent to a Web browser client.

There are two types of EJBs: session beans and entity beans. Session beans only live as long as necessary for a particular application session, whereas entity beans persist between runs of an application. The classic example of a session bean is a shopping cart in an online purchasing system, whereas an entity bean might be the customer. The shopping cart gets filled by the customer, and at the end of the transaction is no longer needed. The customer entity, however, has to exist for as long as that customer exists.

To support the persistence of an entity bean, there has to be a way to store its variable data to disk between uses, and then reload it from disk when needed. You can do this yourself using bean-managed persistence (BMP), or let the EJ shell do it for you using container-managed persistence (CMP). The latter is by far the easier, and typically it will be saved into a relational database such as DB2. All entity beans must have a variable or variables that uniquely identify a particular instance of the bean, so that other Java code can explicitly identify which bean instance they want to work with. For example, a customer bean might key its instances by the customer ID variable.

There are specific classes and methods to tell the EJ shell what entity bean to find (if it is already active) or load into memory (if it is not already active). The ability for client code to find and activate EJBs is part of the base infrastructure, and is supported by Java Naming and Directory Services (JNDS). This is a Java-specified interface for this type of functionality. It is often implemented by vendors such as IBM using LDAP behind the scenes.

All EJBs have two supporting interfaces that client code uses to access the bean: a home interface with lifecycle methods for creating, locating, and removing instances, and a remote interface with the actual business methods. The shell contains the necessary infrastructure and methods for returning instances of classes, which implement these interfaces, to the client. The client code then uses the home interface instance to create or locate the EJB instance it wants, and the remote interface instance to run the business methods. Like RMI, these instances are proxies, with the real work being done in the EJ shell.

After a developer creates an Enterprise Java Bean and its two required interfaces, he or she must also create a deployment descriptor to help anyone deploying the bean into the shell. It is a simple properties file indicating the attributes, such as bean type (session or entity) and persistence type (CMP or BMP). It also states explicitly whether this bean must only be run as part of a transaction, not part of a transaction, or optionally as part of a transaction. There are explicit methods you can implement in your bean to handle various states of transaction management, such as commit and rollback. These will be called for you by the EJ shell, which supplies the transaction manager as part of it. Behind the scenes, IBM uses TXSeries or CICS.

A detailed discussion of EJBs is well beyond the scope of this book, but we do recommend you look into them-after you've mastered Java, Servlets, and JSPs! As of this writing, EJBs were poised to become a major industry standard, with many companies stepping up to creating EJB tools and actual EJBs. Unless things suddenly change (again), these are looking more and more like the future of server-side programming. Servlets and JSPs that leverage EJBs for their data are looking more and more like the future of client-side programming. (Even though the code actually runs on the server, it generates a user interface for the client side.)

Keep an eye on these and numerous other important initiatives at JavaSoft's Web site, www.java.sun.com/products, and IBM's main Java Web site, www.ibm.com/java. Furthermore, IBM's VisualAge for Java product contains tooling support for all of these (including a built-in EJ shell for testing purposes and great EJB creation and deployment tools). Its documentation can be a big help getting started with EJBs.

XML

Since XML has gotten so popular so fast, we thought we should include a brief explanation of it, so you can decide whether to pursue it or not. XML stands for eXtensable Markup Language. Like HTML, it is a tag-based language, but whereas HTML is a specific language with explicit tags relating to the definition of Web pages, XML is a "meta language" with no tags at all! Rather, it is a language for creating other tag-based languages. Anyone can create a new language by simply defining new tags. Here is an example of an XML-compliant language:

<FAMILY>
   <SURNAME>Coulthard</SURNAME>
   <FATHER>Phil Coulthard</FATHER>
   <MOTHER>Christine Coulthard</MOTHER>
   <CHILD>Cassandra Coulthard</CHILD>
   <CHILD>Jennifer Coulthard</CHILD>
</FAMILY>

We just invented this language on the fly, using HTML-style tags that all have a beginning tag and an ending tag. You can see immediately one of the benefits of XML is that data defined with it is usually self-describing and easily readable. That is because an XML document embodies the following:

  • Descriptive information. These are the tags. If well-chosen, they give human readers a clue as to the content.

  • Structure. This is the nesting of the tags. For example, you see in the example that families contain surnames, fathers, mothers, and children. More could be added, and all are optional. This nesting can go as deep as you want, and helps illustrate relationships among the data.

  • Data. This is the actual text between the tags, and is the "meat" of the document itself, usually. You can also specify information in the form of name="value" in

the tag itself, as in <BOOK isbn="12345678">Java By Dummies</BOOK>.

So what does XML have to do with Java? Because XML prescribes explicit rules (like every beginning tag must have an ending tag), it is easy to write generic parsers that can parse any given XML document and present the results either visually in a tree view, or programmatically in a tree-style linked list data structure.

Many Java parsers for XML are available now. They all parse the XML into a Document Object Model (DOM), which is a tree structure matching the XML tag's nested hierarchy. Your Java code can simply call the parser code with a given XML document file name, and then walk the generated DOM to extract the data. You can even insert a Document Type Definition (DTD) at the top of your XML document, which is a set of tags that defines the rules for your tags. If this exists, the parser will use it to verify that the XML document is correct. Here is a complete example including a DTD:

<!DOCTYPE FAMILY [
   <!ELEMENT FAMILY (SURNAME, FATHER?, MOTHER?, CHILD*)>
   <!ELEMENT SURNAME (#PCDATA)>
   <!ELEMENT FATHER (#PCDATA)>
   <!ELEMENT MOTHER (#PCDATA)>
   <!ELEMENT CHILD (#PCDATA)>
   ]>
<FAMILY>
   <SURNAME>Coulthard</SURNAME>
   <FATHER>Phil Coulthard</FATHER>
   <MOTHER>Christine Coulthard</MOTHER>
   <CHILD>Cassandra Coulthard</CHILD>
   <CHILD>Jennifer Coulthard</CHILD>
</FAMILY>

The DOCTYPE tag tells the parser that family structures contain an optional father tag (?), an optional mother tag, and zero or more child tags (*). Also, each tag contains character data (#PCDATA). XML documents that do not have a DTD but are syntactically valid per the XML rules are called well-formed documents. A well-formed document that also has a valid DTD is a called a valid document. If you want to design your own XML language, we suggest a tool called Visual DTD, available as of this writing on the IBM Alphaworks Web site (www.ibm.com/alphaworks).

IBM's Java parser for XML is called XML4J. It is available at www.ibm.com/alphaworks , and in numerous IBM products, such as IBM's WebSphere Application Server. It is a reusable Java package that will parse any XML into a DOM tree that your Java code can subsequently traverse. It also comes with some example Java applications that do just this. For example, the TreeViewer application displays the incoming XML document graphically, in a tree view.

There are many other Java parsers of XML, including one built into Microsoft Internet Explorer. Many companies are involved in the XML standard, and almost all now endorse it. The standard comes from W3C, the consortium of companies that oversees Internet-related standards.

To understand the significance of XML, think of it as a platform-neutral, industry-wide way of expressing data. That is its main role, and one that has given it such huge popularity so quickly. You have probably written applications that either exchange data with other applications, or save data between applications or sessions of the same application. If you write the code that creates the data and the code that consumes or reads the data, this is reasonably easy, although even then, you might have to worry about code pages and evolving releases of your application. If you generate data to be consumed by code written by others, such as in a business-to-business environment, than you know from experience how painful it can be. The consumer of your data has to be intimately aware of the format you invented to save that data.

This is the power and value of XML. You can generate data that is self-describing, and anybody can write code to read it, without any previous knowledge about the data's format. This is especially easy if an XML parser is utilized, such as XML4J.

XML is seeping into every product that has to store data. For example, Rational Rose will use XML to store your OO model data, so any other tool can easily read it. EJBs use XML to define the deployment descriptor. IBM's MQSeries uses XML to pre-define message formats. These are examples of another benefit of XML, beyond your ability to generate self describing data: the ability for all tool vendors to work with the data produced by all other tool vendors. If everyone talks XML (specifically, languages written in XML), then everyone talks.

The biggest benefit for you, though, is in data expression. DB2 and other database vendors already support the ability to express the results of a query in XML, using column names as the tags and column definitions as the DTD information. Further, databases will also support the ability to parse any XML file and create tables on the fly to store the XML efficiently. The tags become columns, the data becomes rows.

There are other related technologies, too, that are very cool. These include XSL

(eXtensable Style Language) to map one XML language to another (or to HTML) using a descriptive tag-based language. Thus, you can do a database query, get the results in XML, and use an XSL transform to map that data to HTML, which is then displayed on a Web browser, for example.

XML is sweeping the world. Check out the details at www.w3.org and www.ibm.com/xml.


VisualAge For Java

One of the big benefits to you as a Java programmer is the choice you have in selecting a Java development tool. There are dozens of them on the market, and you really can use any one of them you want. The majority of these tools run on Windows, although a few of them also run on other "client" platforms. They all apply to you, since the output of them all is Java, and Java is portable. Hence, with a mounted drive to your OS/390 or OS/400 system, you can copy the resulting class file to your server and run it there without change.

IBM's offering in the Java IDE (Integrated Development Environment) space is unique in that it offers you explicit support for doing OS/390 and OS/400 Java programming. You do not "need" this special support, but like all good productivity aids, it is hard to live without once you've seen it. For details on the explicit OS/390 and OS/400 support in VisualAge for Java, see appendix D and appendix E. The following sections give you a very fast tour of the base product. This information is accurate as of Version 3.0 of VisualAge for Java, but as you read this, there are probably many enhancements and changes.

Versions of VisualAge for Java

VisualAge for Java (or VAJava, as it is affectionately known,) comes in three versions, as of this writing. They are described in Table 14.9.

Table 14.9: VisualAge for Java Versions.
Version | Description
Entry | Same as Professional, but with an upper limit on how many classes you can create. This is a "free" version, downloadable from the Web site www.ibm.com/software/ad/vajava.
Professional | A reasonably cheap, full-function version of the product that includes everything and more that a Java programmer could ever want. This includes the Integrated Development Environment (IDE), incremental and automatic compilation, versioning, debugging, SmartGuides ("wizards") creation, servlet/JSP support, and even the award-winning Visual Composition Editor (VCE) for easy WYSIWYG designing of GUIs.
Enterprise | This builds on the Professional version, adding "grown-up" functionality such as team support and connectors (SmartGuides for generating Java code to access existing assets such as data, programs, transactions, Lotus Notes, SAP, MQ Series, and many others). This is also the version where you will find OS/390 and AS/400 Enterprise Toolkits, and EJB tooling.

When you get VisualAge for Java, it comes with the book Getting Started, which is also available online from the Help pull-down of the main IDE window. Our purpose is not to teach VAJava here, but rather to point out some of its highlights.

The VisualAge for Java Repository

Perhaps the most unique aspect of VisualAge for Java over other Java tools is that it stores all of your Java code inside a single repository by default (although you can split it over multiple repositories, if you prefer). This master repository contains all the versions of all your Java classes. It also contains all the classes that the JDK supplies and that IBM supplies.

You don't work with the whole repository at once, but rather with a subset of it called the workspace. You decide which projects, packages, classes, and methods to include in your workspace by "importing" them from the repository. When you don't need them again, you delete them from your workspace, but they remain in the repository. Note that a project in VAJava is a unique and convenient way to group multiple related packages. For example, each major application you create would likely be created in its own project.

When working with VisualAge for Java, your main focus is the Workbench window, which offers a view of all the projects in your workspace. You can expand any project to list all the packages in it. You can further expand any package to list all the classes and interfaces in it. Finally, you can expand any class to list the variables and methods in it. When you select any class or method, the edit pane shows the code inside that class or method. You can change the code by editing it in the edit pane and selecting Ctrl+S to save your changes. Figure 14.11 shows an example of the VAJava Workbench window.

Figure 14.11: The main WorkBench window in VisualAge for Java.

Notice the use of superscript letters (like T) in Figure 14.11. Once you learn to interpret these, they help you see information at a glance. For example, the little F in front of aActionListener indicates it is a field (a variable), while the little M in front of addActionListener indicates it is a method. The little T after aActionListener indicates it is uses the transient modifier. Note that the default view is by project, but you can use the notebook tabs to change the view to be by package, by class, or by interface.

When you edit classes, interfaces, variables, and methods in the Workbench window, you are working on a particular edition of them in your workspace. At any time, you can " version" the current state (edition) of a project or package or class, which places that version of it (and all its subordinate elements) into the repository. The previous version is still there, with a different version number. If you continue editing after versioning, a new edition is created in the workspace, and eventually you can version it again, producing yet another stable checkpoint in your work. At any time, you can browse the repository and delete old versions. You can also revert your workspace back to a previous version.

The Enterprise edition of VAJava adds built-in team support to the repository and workspace. This allows you to place the repository on a different system on your LAN, and have multiple programmers access it remotely. As they work on local editions of the code in their private workspaces, they occasionally version it, making their changes available to the rest of the team, who subsequently replace their workspace versions with the updated copy in the repository. This repository and workspace architecture is very powerful because it allows for complete control over the development lifecycle of a Java application. For example, it allows for very powerful "where used" searches.

To work with existing Java code in VisualAge for Java, you have to import it from the file system into the repository. To ultimately deploy the result, you have to export the code out to the file system. You can export code as Java source, Java class files, or Java Archive (.jar) files. To move code from one repository to another, you can also export it as a repository (.dat) file. All these can also be imported from the file system into your repository.

Creating, Editing, Running, and Debugging Inside VisualAge for Java

The WorkBench window in the VisualAge for Java IDE includes menu items and toolbar buttons for creating projects, packages, classes, interfaces, applets, applications, methods, and variables. These launch SmartGuides, which prompt for relevant information and then generate the necessary template code to get you started quickly. For example, Figure 14.12 shows the Create Method SmartGuide.

Click To expand
Figure 14.12: VisualAge for Java's Create Method SmartGuide.

Notice how easy it is to select the modifiers your method will support. These SmartGuides take much of the drudgery out of Java coding. They are context-sensitive, so they will create the new method inside the class you had selected in your WorkBench window.

Of course, you still have to write your own Java logic! This is done simply by typing inside the edit pane when the appropriate method is selected. You can also "open" a particular project, package, class, interface, or method to get a new mini-workbench window that is scoped to just that item. This mini-workbench usually includes some new tabs that offer additional functionality. For example, Figure 14.13 shows a window opened for a package. Notice how this gives you a nice class hierarchy view, so you can see easily what classes extend what other classes.

Click To expand
Figure 14.13: An open package window in VisualAge for Java.

In all views, the edit pane is a full-fledged editor with some useful help, such as a code assist to help you "finish" your partial Java statements. The editor also checks your changes at save-time and reports any errors in an error-list window for easy resolution. Perhaps the most important aspect of editing your Java source, though, is that to subsequently test it, you simply press the running-man icon in the left-most position of the toolbar. This launches your Java application by calling the main method in the class. There is no need to do a separate javac compile step because VisualAge for Java always keeps a runnable bytecode version of your code handy, and incrementally updates it whenever you save changes in the editor.

Occasionally, you might find yourself with a bug in your code. (Well, such is the rumor.) You will need a debugger. This is when a Java IDE really pays for itself! For example, in VisualAge, to set a breakpoint, you simply double-click in the editor, just to the left of the line where you want to stop. Figure 14.14 shows the edit window pane with a breakpoint set.

Click To expand
Figure 14.14: VisualAge for Java's edit pane with a breakpoint.

When you run the program and hit that line, the program stops, and the debugger comes up so you can interrogate your variables. Figure 14.15 shows the VisualAge for Java debug window.

Click To expand
Figure 14.15: VisualAge for Java's debug window.

Within this debug window, you can display variables (all vari ables in scope are listed in the middle pane, and selecting one shows its content in the right-most pane), change variables, and change the code (in the edit pane on the bottom). If you do change the code, it will be immediately effective as you continue running/debugging. Of course, you can also set new breakpoints, step line-by-line through the code, run to a breakpoint, and so on. All the usual debug functionality is built right in.

It is important for you to realize that your code is running and debugging right inside the IDE, even if you ultimately plan to export it to OS/390 or OS/400 and run it there. If you want to actually run or debug it on OS/390 or OS/400, that is possible too with the Enterprise version of VAJava. This is covered in appendix D and appendix E.

The VisualAge for Java Visual Composition Editor

Finally, to wrap up this introductory tour of VisualAge for Java, its Visual Composition Editor (VCE), often called the Visual Builder, has arguably been its most famous function. It is widely considered to be the "trademark" of VisualAge. The VCE is primarily used to create graphical user interfaces using AWT and Swing (as discussed in chapter 12). Like all GUI Builders, the VCE has a palette of parts that you drag to the canvas, which is a frame window for an application or an applet panel for an applet. You can subsequently double-click the parts to change their properties, such as their color, font, and text. You can also use the myriad of alignment tools on the toolbar to align them just so.

To get to the VCE for a new application, select the New Application SmartGuide, and decide between Swing or AWT. The next page of the SmartGuide gives you a number of options for creating a good-looking skeleton of a real GUI application, as shown in Figure 14.16.

Click To expand
Figure 14.16: Using the New Application SmartGuide in VisualAge for Java.

When you press the Finish button, the VCE comes up, and you are off and running. For existing applications or applets, select your class that extends the Java frame or applet class, right-click, and then select Open. Figure 14.17 shows an example of the VCE.

Click To expand
Figure 14.17: The Visual Composition Editor in VisualAge for Java.

What is significant about the VCE, and what sets it apart from other GUI builders in other products, is that it allows you to go well beyond the visual layout.

For example, you can drop non-visual code (class instances) outside the canvas area, and you can connect any two parts together, whether they are visual or non-visual. The connection is started by right-clicking and selecting Connect on any part. This allows you to choose a method, property (a variable with get and set methods), or event (discussed in chapter 12) from a list generated for that part. You then drag the mouse to a target part and select the method, property, or event to connect to. (The term part is used here generically; really, these are all Java objects.)

Suppose you connect the actionPerformed event of a push button to the printReport method of a non-visual part. This would generate the code to call the printReport method on that target object whenever the button is pressed. Visually, you would see a line ("wire") drawn in the VCE from the push button to the non-visual part. Sometimes these method calls need parameters, and if you have not supplied them yet, your line will be dotted to indicate this. They can be hard-coded by double-clicking the line and typing in the parameters, or they can be supplied by connecting the line itself to a property of another part.

For example, if printReport required a customer ID number and that number existed in an entry field, you could connect the wire to the entry field and select the entry field's text property. This will generate code to furnish the parameter from a call to getText on the entry field object, and you will see another line from the first connection line to the entry field.

This is quite fun, but if you want to drop back to raw editing of the code, you can do so at any point by clicking on the Members tab at the top. You can toggle back and forth as you like. By the way, all the Java parts in the JCE are Java beans, and every class you create in VisualAge for Java is also Java-bean conformant. You can add new beans to the palette easily.

Other Functions in VisualAge for Java

Table 14.10 lists just some of the additional functionality you will find in VisualAge for Java.

While there are many tools to choose from when doing Java development, VisualAge for Java has become pretty much a standard. It is a favorite among industry critics who have showered it with awards, and it has a very significant user base. For OS/390 and OS/400 programmers especially, it has become the de facto standard for Java development. This is not only because of its great base features, but also because of its great built-in support for both these systems. The details of this unique support is covered in appendix D and appendix E. This support is not included in the Professional version.

Table 14.10: Additional Functionality in VisualAge for Java.
Function | Version | Description
Scrapbook | All | A window for quickly coding and running stand-alone Java statements for testing or learning purposes.
Data Access beans | All | Beans in the palette for quickly accessing existing data via JDBC. Includes an SQL statement wizard.
Servlet and JSP support | All | WebSphere runtime is built into the IDE. Also, a debugger for JSPs (the JSP Execution Monitor).
SQL/J | All | Create, edit, version, run, and debug SQL/J source just as you would raw Java source.
Enterprise Java Bean support | Enterprise | There is very robust built-in tooling for EJBs. IBM Enterprise Java Server is built into the IDE for local testing purposes.
Connectors | Enterprise | Easily access your Lotus, SAP, MQSeries, CICS, IMS, DB2, and other important assets.
High-Performance Com-piler | Enterprise | If interpreted Java is too slow, then compile it into traditional executables or DLLs on OS/390, Windows, OS/2, or AIX. OS/400 has its own built-in compiler (discussed in appendix E).
VisualAge Persistence | Enterprise | Want to map Java objects to database tables? Database tables to Java objects? This is a combination of both. It is a grown-up tool for grown-up database access.
CORBA IDL | Enterprise | Create IDL from your Java code, within the IDE. Manage the IDL in the repository. Import your favorite Java ORB for testing purposes.
Enterprise Toolkit for OS/390 and AS/400 | Enterprise | Right from your IDE, you can export selected packages and classes to the remote system, remotely statically compile them, remotely run them, and remotely debug them.

One last point: As of this writing, VisualAge for Java is available for all 32-bit versions of Windows, for OS/2, for AIX, and for Linux. It also supports OS/390, OS/400, and Sun Solaris as remote deployment systems.


Summary

In this "wrap up" chapter, we briefly covered the following topics:

  • Static initializers.

  • Java Native Interface (JNI).

  • Remote Method Invocation (RMI).

  • Object serialization.

  • An overview of the packages in JDK 1.1.x and JDK 1.2.x.

  • Working with flat-file system objects.

  • Java beans.

  • Servlets JavaServer Pages.

  • Enterprise Java Beans (EJBs).

  • XML.

  • VisualAge for Java.

The intent of this chapter is to point you in the right direction for learning more about these topics, rather than offer detailed information. You are probably ready now for a break from all this reading-before diving into your next Java book!

Thanks for coming along for the ride. We are big believers in Java. It is a single technology that can span all your information technology requirements-from personal devices to rich GUIs to mission-critical enterprise applications. We are big believers in COBOL, too. However, eventually, Java will play a role in your applications. Java will change the face and impressions of OS/390 and OS/400-and you will be there, putting your new Java skills and enthusiasm to work! Knowing both COBOL and Java is a big asset. Now it's time to talk to your manager about that raise…


Appendix A: COBOL For OS/390 Flow Of Control

This appendix reviews the latest features of IBM COBOL on OS/390, and what a typ- ical OS/390 COBOL application leveraging these features might look like.

IBM COBOL For 0S/390 & VM Review

IBM COBOL for OS/390 & VM is IBM's migration path from the following:

  • OS/VS COBOL.

  • VS COBOL II.

  • IBM COBOL for MVS and VM.

If you are still using one of these flavors of COBOL, you should consider migrating to IBM COBOL for OS/390 & VM. Why? Among other reasons, it is the only version of COBOL that IBM is investing in and enhancing.

Here are some of the enhancements you will enjoy with this new COBOL:

  • Recursive calls.

  • Year 2000 date-handling capabilities.

  • A set of intrinsic functions for string handling, financial capabilities, statistical functions, and mathematical formulas.

  • Access to Language Environment (LE) services.

  • Error-handling routines in COBOL.

  • Improved interoperability with other languages via LE.

  • Dynamic Link Library (DLL) support for true reuse.

  • Object-oriented (OO) COBOL support for creating classes, methods, and objects in COBOL. (For more details, see appendix C.)

  • A path to emerging ANSI COBOL standards.

IBM COBOL for OS/390 and VM works in the Language Environment (LE) of OS/390 and VM, which is an environment that allows for easy interoperability and coexistence of programs written in various languages, such as COBOL, PL/I, C, C++, and, of course, Assembler.

Typical COBOL Application Structure

In OS/390 and VM, you compile your COBOL source programs into object modules, which in turn are link-edited along with other object modules into an executable load module. Inside the executable load module, you make static calls among COBOL programs, and among executable load modules, you make dynamic calls, as shown in Figure A.1.

Click To expand
Figure A.1: Calling between COBOL programs.

Both static and dynamic calls use the CALL statement, but static calls use a literal name for the program to call, while dynamic calls use a variable name. Also, the NODYNAM and DYNAM compiler options are used to implement the choice of calling method. The tradeoff between using static versus dynamic is the classic decision among performance, memory footprint, and maintainability.

Static calls offer the best performance, but increase the size of every load module that re-uses the same COBOL program. This makes maintenance more difficult, as it requires every load module using the COBOL program to be link-edited again after updating the program.

Dynamic calls cost a performance hit the first time the program is called (because its load module must be loaded at that time), but allow you to reuse this COBOL program in many applications without increasing the size of every load module that calls it. Maintenance is easier, too, because you can update the COBOL program and just link-edit the load module it is in, not all the load modules that use it.

It would seem, then, that a world where the main priorities are reusability and easy maintenance of those reusable programs would tend to favor dynamic calls. Indeed, most COBOL programmers do.

Looking inside a particular COBOL source program, you will find calls to paragraphs within the PROCEDURE DIVISION. The paragraphs can only be called (via the PERFORM statement) by code inside this COBOL program, not directly from other COBOL programs. Paragraphs do not have local variables scoped to them; they cannot be passed parameters and they cannot return a value to the caller.

A relatively new option is the use of nested programs. These allow you to nest a whole COBOL program inside another one. There can be multiple nested programs inside one program, and nested programs can, in turn, have their own nested programs, as shown in Figure A.2. Nested programs cannot be called directly from other COBOL programs, but only by their parent or enclosing program, or by their sibling programs if defined with the COMMON attribute. They are called using the CALL statement. Recursive calls are not allowed for either nested programs or programs containing nested programs.

Figure A.2: Nested COBOL programs.

The nice thing about nested programs is that they allow you to encapsulate a function inside your program-a function that, like all COBOL programs, can take parameters and return a value to the caller. Further, you can localize the variables used by that program to that program only.

New Packaging Option: Dynamic Link Libraries (DLLs)

New in Version 2 Release 1 of IBM COBOL for OS/390 and VM is the ability to package your COBOL programs into something called a Dynamic Link Library or DLL load module. (DLLs are not supported in VM.) Recall that dynamic calls to other load modules offer more flexibility for maintenance and memory size than the alternative of static calls to link-edited object programs.

One thing is not so appealing with load modules, though. You don't call the object programs directly inside the load module; you really call the load module itself. This has the effect of calling the first object program in the load module, which in turn can call the subprograms in the load module. But what if you wanted multiples of the object programs inside the load module to be called directly by other load modules?

For example, you might have a number of programs that all perform different actions on a customer database, such as reading a customer, adding a customer, deleting a customer, updating a customer record, printing a customer detail report, and so on. Many of your applications might need to use these routines. If would be nice if each object program could be directly callable, instead of forcing entry through the first object program and then using a routing mechanism. A common routing mechanism is passing a parameter identifying the "operation" requested, and then performing the call to the subprogram from the main program, or having to make each a separate load module.

DLL load modules offer the following:

  • Multiple object programs can be combined into a single DLL load module.

  • Users of the DLL load module can directly call any of the object programs in the DLL.

To enable these capabilities, the system maintains a list of the object program names inside the DLL load module. This is done by means of a new pre-link step, which produces the list in a DLL definition-side deck. This is a list of all the "exported" object programs and data items contained in the DLL. The list is in the form of import control statements. This is then used as input in the pre-link step of any executable load module or other DLL that wants to use this DLL. The pre-linker resolves any references to those names, or symbols, as they are official called.

The following steps are needed to produce a DLL:

  1. Compile the source programs to be part of it, using the DLL, RENT, and EXPORTALL compiler options. This produces DLL object programs.

  2. Pre-link these DLL object programs into a single object module and a DLL definition-side deck.

  3. Link-edit the object module from step 2 into a DLL load module, specifying the RENT and DLLNAME options. (DLLs have to be re-entrant.)

The steps for a load module to use a DLL by calling one or more of its program objects are as follows:

  1. Compile the program that calls programs in the DLL, with the DLL and RENT options. To call a DLL program, your own module must be compiled with the DLL option (well, there are ways around this), even if it is destined for a traditional executable load module.

  2. Pre-link all the object programs in your target executable load module into a single object module, concatenating the DLL definition side deck of the DLL you will be calling, with your object modules, as the SYSIN input.

  3. Use the linkage editor to turn the object module from step 2 into an executable load module.

This whole process is shown in Figures A.3a and A.3b. Note that DLLs can also use other DLLs (and usually do), simply by including the DLL definition decks of the other DLLs in the pre-link step.

Click To expand
Figure A.3a: Creating DLLs.

DLLs are a good thing, and something that other systems have had for some time. Typically, applications will have one initial executable load module that makes heavy use of numerous DLLs, which in turn use numerous DLLs themselves.

Figure A.3b: Using DLLs.


COBOL For OS/390 Flow Of Control

With COBOL, the first program in the initial load module gets control from the command line or job step in the first paragraph of the PROCEDURE DIVISION. From that point on, control is passed as follows:

  • To other paragraphs in this PROCEDURE DIVISION (via PERFORM).

  • To other programs nested inside this initial program (via CALL).

  • To other programs link-edited into this executable load module (via static CALLs).

  • To other programs inside DLLs pre-linked to this executable load module (via static CALLs).

  • To other executable load modules (via dynamic CALLs). There are restrictions on this when using DLLs in your application. See the COBOL Programmers Guide for details.

Note

DLLs are not callable directly from the command line. They must be used by a load module that calls the programs inside the DLL, or by another DLL that calls programs inside the DLL.

Figure A.4 shows this flow of control. Since programs compiled with the DLL option must be re-entrant, and since programs containing nested programs must not be entrant, you cannot have nested programs inside DLLs, or even within source programs that call DLLs.

Click To expand
Figure A.4: Flow of control.

Distributing Your COBOL DLLs

Once you have a COBOL DLL modules that contains a number of useful programs, you might want to sell it, or at least package it for reuse by others. This is easily done. For anyone who wants to use your DLL in their own programs, you would need to supply two, or possibly three, things:

  • The DLL load module itself.

  • The DLL definition-side deck containing the IMPORT statements for all exported names.

  • Copy members of WORKING-STORAGE definitions corresponding to the LINKAGE SECTION for the called programs, for parameter-passing purposes.

A user of your program would then do the following:

  • Include the copy member in their source (COPY), so they could easily supply the expected parameters to the programs.

  • Specify the DLL definition-side deck when prelinking their load modules or their own DLLs.


Summary

There are new functions in COBOL and new functions in the Language Environment that you might not have started exploiting yet. We recommend you have a look at them and treat them as additional tools to help you evolve your code base into a well-partitioned collection of reusable building blocks.


Appendix B: ILE COBOL Flow Of Control

This appendix reviews the latest features of IBM COBOL on OS/400, and what a typical OS/400 COBOL application leveraging these features might look like.

ILE COBOL For OS/400 Review

We believe that you should continue your code migration to ILE COBOL from

COBOL/400, even if you are eventually destined for Java. Moving from COBOL/400 to ILE COBOL will give you short-term quality, maintainability, functionality, and performance benefits. Such a progression will position your code base to make selective replacements with new Java code. This is not possible if you have a monolithic COBOL/400 application model. Further, coexistence and interoperability of COBOL and Java is a focus item for the continuing enhancements in ILE COBOL.

COBOL/400, or as some call it, "OPM COBOL," has been stabilized by IBM. Enhancements are only made to the ILE COBOL compiler. If you have COBOL/400, then you already have ILE COBOL because the two are packaged together. To help with your decision to use ILE COBOL instead of COBOL/400, the following sections provide a partial list of some of the enhancements in ILE COBOL (beyond the benefits of ILE, which are describe later in this chapter.)

V3R1 adds these enhancements:

  • EXTERNAL data items and files, allowing access to these across a run unit.

  • Nested source programs, allowing restricted access to resources.

  • Nested COPY statements.

  • Procedure pointers, useful for calling some system APIs.

V3R2/V3R6 or higher adds these enhancements:

  • Double Byte Character Set (DBCS) data type support.

  • The ability to pass parameters by value, instead of only by reference.

V3R7 or higher adds these enhancements:

  • Four-digit century support.

  • Internal floating-point support.

  • External floating-point data items.

  • Data area support.

  • The first wave of intrinsic functions.

  • Library-qualified program and data area references.

V4R2 or higher adds these enhancements:

  • User-defined data types, via the TYPEDEF clause on data items.

  • Null-value support for database fields.

  • Locale support for internationalization.

  • Date, time, and timestamp data items.

V4R4 or higher adds these enhancements:

  • Thread safety support for improved interoperability with Java, C, C++, and Domino.

  • PIC(31) support.

  • Euro currency support.

Of course, each release offered many other smaller enhancements, and future releases will continue to add enhancements, and hence improve the value of this new compiler.


Traditional COBOL/400 Applications

In a typical, traditional COBOL/400 application, you might have a CL program that gets initial control and calls the appropriate COBOL program object, or other programs as required. The COBOL program, of course, is huge. It contains dozens, if not hundreds, of paragraphs in the PROCEDURE DIVISION. A large application might have dozens of these COBOL programs, but due to the overhead of calling them, each does a significant amount of work when called, usually driving an entire piece of the application. Reuse is achieved by COPY statements.

The initial CL program might put up a main menu and process the selected option by calling the appropriate COBOL program. It also, of course, performs file overriding and library-list manipulation. Such a scenario is illustrated in Figure B.1.

Click To expand
Figure B.1: A traditional COBOL application.
Note

The application is "scoped" to the job, so common resources, such as open data paths and file overrides, are available and applicable to all the programs in the job.

You probably also have main COBOL programs that call COBOL subprograms. Program-to-program calls like this are said to be dynamic because for each initial call, the runtime has to initially find the called program, verify authority and access, allocate storage, load it into memory, and so on. The subprograms remain in memory and retain their data items' states with subsequent calls. The key thing is that with COBOL/400, you can write large source programs that are compiled into large program objects, as shown in Figure B.2.

Click To expand
Figure B.2: Compiling a traditional COBOL program.


New ILE COBOL Applications

Figure B.3 illustrates what an ILE COBOL application might look like. There are some major differences here, compared to the previous setup. First, ILE redefines the compilation unit so that rather than compiling a source member directly into a program object (*PGM), in ILE you compile a source member into an intermediate module object (*MODULE) using the CRTCBLMOD (Create COBOL Module) command. These modules are not shippable, and you cannot call them from the command line as you call a program object.

Click To expand
Figure B.3: An ILE COBOL application.

Rather, they are designed to be subsequently "bound together" into a program object (*PGM) using the CRTPGM (Create Program) command. We like to think of this binding as "gluing," since you are effectively gluing multiple objects together to form a single entity.

There is also a CRTBNDCBL (Create Bound COBOL) command for creating single-module program objects in one step, but you should only use this as a short-term solution as you convert to ILE COBOL. Single-module program objects are legal, but not very interesting. They do not exploit the potential or the intention of ILE.

ILE COBOL Program Objects and Modules

The power of ILE is that modules compiled from different source members and even different languages, such as ILE CL, ILE RPG, ILE C, or ILE C++, can be bound together to form a single program object. This program object is no different than those you are used to-you call it from the command line or via a command (*CMD) object. You might wonder, though, which module gets control from the command line. By default, the first one specified on the CRTPGM command that created it gets control.

Why, in COBOL/400, do programmers write such large programs? Performance, of course. It is simply too expensive to make calls between programs using the external CALL statement. There is a downside to using these large programs, however, that programmers have been forced to live with. The main disadvantages are in maintainability and ease of development:

  • Maintainability. You are new to the job, and you have been given the assignment of fixing an identified bug in someone else's code. (Why do "other" programmers always write terrible code? Why is the driver in front of you always too slow, and the one behind you always too fast?) Your supervisor says, "Here are their 20,000 lines of source code. Go for it!"

  • Ease of development. Suppose there are 12 of you working on a hot new project, but there is only one source member. You'll have to wait your turn to edit it.

ILE set out to improve on this development and application model. It strikes at the very heart of why programmers write large, monolithic programs-the performance of interprogram calls.

ILE helps inter-program performance by allowing very fast calls between COBOL source programs (*MODULES) that are in the same program object (*PGM). These are fast because all their linkage information is determined at binding time (CRTPGM), instead of at runtime. Calls between modules like this are known as static calls versus the dynamic calls you are used to. Modules can call each other without the performance penalty commonly encountered with program calls. This allows you to write more, but smaller, COBOL source programs to help with modularization. All these modules are eventually combined to form a single program object that you can ship.

Here is how ILE solves the problems mentioned earlier:

  • Maintainability. You are new, but you are good! You found the bug in that other programmer's code because you only had to look at 2,000 lines of code, not 20,000 lines of code. And you did it before lunch. Take the rest of the day off.

  • Ease of development. All 12 programmers get their own source member to work on. Their work is combined later by binding the 12 modules into one program object. (Of course, these 12 programmers need to talk once in a while to know what the others are doing.)

You are probably wondering how all of these modules "call" each other. They use ILE procedures. This is simply another term for COBOL source programs or modules. In the general ILE world, these are procedures within modules. Each COBOL source program, and hence module, contains only one callable entry, which is the COBOL "program" itself, as named in the ID DIVISION.

Do not confuse ILE procedures with the PROCEDURE DIVISION within your program. There is a one-to-one correspondence with these non-nested programs, but they are different. The former refers to the entire compiled module. So now COBOL code in one source member can call a COBOL source program (that is, an ILE procedure) in another source member directly, without the performance overhead of a full external program call.

Note that ILE COBOL also allows you to nest one COBOL program inside another, and while both are considered ILE procedures, these between-module calls can only be to the outermost program per module. Other languages, like ILE RPG, allow multiple procedures per module, each of which can be called directly from another module. However, even at one externally callable procedure per module, you are way ahead of the old pre-ILE model, where you could only call from *PGM to *PGM.

Inter-module calls within a *PGM object are done using the CALL LINKAGE TYPE IS PROCEDURE statement, providing better performance than a full external program call using CALL LINKAGE TYPE IS PROGRAM. This is because at bind time (CRTPGM) all calls between modules are resolved to actual addresses. The overhead to resolve addresses is done once at build time, rather than at runtime.

ILE COBOL Service Programs

If you want to facilitate reuse of your new, small, COBOL source program by more than one application or even *PGM object, you can either bind the same module into multiple *PGM objects or, much preferably, put them into a single service program. The benefit is that your programs do not all grow in size with multiple copies of the same module.

Service programs (*SRVPGM) are new entities in ILE. Like programs, service programs can consist of one or more modules. Unlike programs, they cannot be called from the command line. Rather, they are intended to be called programmatically by code inside one or more *PGM objects (or other *SRVPGM objects). Once again, it is the ILE procedures inside the service programs that are called.

Now, instead of "gluing" this reusable module to all your programs, you instead link your programs to this service program. This is a logical link, not a physical gluing together, so even if multiple programs in a single application use the same service program, there is only one copy of the service program in memory for that application. This is shown in Figure B.4.

Click To expand
Figure B.4: COBOL reuse through service programs.

An important aspect of having these modules inside programs and service programs is that, if you change the source code for one module, you can recompile that module and selectively replace or update it inside the program or service program, using the UPDPGM and UPDSRVPGM commands. This avoids the need to recompile all of the source code that comprises the program or service program.

The intention of grouping these modules into *PGMs and *SRVPGMs is that you can take a monolithic old program, divide it into smaller modules, and then bind them back together into a single program with zero or more service programs. This allows each source member to be smaller and more isolated from the rest of the code, making maintenance easier, and hopefully making reuse of code between applications easier, too. Furthermore, you can take what was formerly a group of multiple programs and think about binding and linking pieces of them together into a new, smaller collection of *PGMs and *SRVPGMs, thus reducing the amount of *PGM-to-*PGM calls you have to make. Commonly needed routines can be packaged as service programs, instead of using the old copy-and-paste or COPY statement tricks that lead to maintenance problems and bloated programs.

Each *PGM that uses the same common *SRVPGM receives its own copy of the variables in it at runtime-that is, as long as the programs are in separate jobs, or the service program uses a named activation group, or the service program specifies *CALLER for the activation group and the program using it uses a uniquely named activation group. Service programs are the best examples of code reuse: there is only one copy of both the source and the object on the system, no matter how many programs use it. This is also a big plus for maintenance. If the service program has to be updated, in many cases it can be replaced without requiring the reconstruction of all programs that use it.

ILE Activation Groups

Finally, the initial ILE application picture in Figure B.3 shows an activation group inside a job. An activation group is another a new construct for ILE. You can have more than one activation group per job, and all of your application's resources can be scoped to one activation group, as opposed to the entire job. Resources in one activation group do not get shared with another activation group. This allows for more fine-grained control over important aspects such as file overrides, open data paths, and commitment control.

Activation groups can be named so that they persist for the life of the job. They can also be defined so that they are created and destroyed with the life of the program. For performance reasons, it is very important to use named activation groups, which is not the default. Activation groups are not physical objects you can see on disk. Instead, they are created by the system at runtime as needed, and deleted when your application ends. You identify the activation groups you want to use by naming them on the ACTGRP parameter of the CRTPGM or CRTSRVPGM commands. The use of activation groups is essential to understanding and to mastering ILE, and all ILE books and manuals discuss them.


Compiling COBOL

To recap, in ILE COBOL, both *PGM objects and *SRVPGM objects are comprised of one or more bound modules. Each compilation unit-or *MODULE-is compiled from a corresponding source member, using CRTCBLMOD.

The term bound implies that these modules are glued together using the CRTPGM or CRTSRVPGM com- mands. This effectively takes multiple separate compilation units (that is, modules) and binds them into a single unit (a program or a service program). Other service programs required by this program are linked into the program by specifying them on the CRTPGM command, as shown in Figure B.5.

Click To expand
Figure B.5: Compiling programs and service programs.


ILE COBOL Flow Of Control

With ILE COBOL, in the main module of the CPP (Control Point Program) program object, your program gets control from the command line in the first paragraph of the PROCEDURE DIVISION. From that point on, control is passed as follows

  • To other paragraphs in this PROCEDURE DIVISION (via PERFORM).

  • To other programs nested inside this initial program (via CALL).

  • To other programs (ILE procedures) bound into this *PGM (via static CALLs).

  • To other programs (ILE procedures) inside *SRVPGMs linked to this *PGM (via static CALLs).

  • To other *PGM objects (via dynamic CALLs).

Figure B.6 shows this flow of control.

Click To expand
Figure B.6: The flow of control in an ILE COBOL application.

Running a COBOL Program

You call a COBOL program from the CL command line by using the CALL command and passing the parameters, like this:

CALL PROGRAM1(100 200);

This passes control to your program. Specifically, it passes control to the entry module in the ILE COBOL program that you specified on the "Program entry procedure module (ENTMOD)" parameter of the CRTPGM command. This defaults to the first module specified in the MODULE parameter list. Control goes to the first paragraph in the PROCEDURE DIVISION. The code in the main procedure may then make calls to other COBOL source programs within the program object or to any linked service programs.

Distributing Your COBOL Service Programs

Once you have an ILE COBOL service program that contains a number of useful procedures, you might want to either sell it or at least package it up for reuse by others. This is easily done. You would need to supply the service program object itself (*SRVPGM) to anyone who wants to use your service program, and perhaps some documentation describing the exported ILE procedures inside it, and the parameters they expect. You would probably also supply copy members with the parameter declarations for the caller's WORKING-STORAGE SECTION. A user of your service program would then specify the service program's name on the BNDSRVPGM parameter of the CRTPGM command when creating a program that uses your service program.

For your own maintenance purposes, you might want to investigate the use of the binder language. This is a source member language for naming all the exported procedures in your service program. You can then specify this on CRTSRVPGM's SRCFILE and SRCMBR parameters. Here is a short example of binding language source, for exporting a proce-dure (named in the IDENTIFICATION DIVISION) called RJUSTIFY:

STRPGMEXP SIGNATURE('RJUSTIFY')
   EXPORT SYMBOL('RJUSTIFY')
ENDPGMEXP

Making procedures available to users of the service program involves either naming the procedure in the binding language as shown above or specifying EXPORT(*ALL) when creating the service program. This makes the procedure accessible to users outside of the service program. This is equivalent to Java's public modifier for methods.

Another ILE construct worth investigating is the use of binding directories (*BNDDIR). When creating programs and service programs, you can either specify all of the modules and service programs explicitly with the MODULE and BNDSRVPGM parameters, or externally, by specifying a binding directory on the BNDDIR parameter.

Binding directories list modules and service programs. You create a binding directory with the CRTBNDDIR (Create Binding Directory) command, and populate it with module and service program names using the ADDBNDDIRE (Add Binding Directory Entry) command. This is a good way to ensure that your programs and service programs are always re-created properly, without having to rely on all programmers knowing exactly what module and service programs are needed when recompiling and recreating the objects.

We also recommend you use a change-management tool to help you work with the many more parts you have in ILE and the additional complexity ILE adds to compiling your applications. There are many good change-management tools on the market. IBM's product is Application Development Manager (ADM). If you are using IBM's CODE/400 (www.ibm.com/software/ad/varpg) for your COBOL development (which we recommend), it offers integration with all of these. CODE/400 is IBM's follow-on to SEU, SDA, PDM, and the system debugger. Whether you are using ILE COBOL or OPM COBOL, you should have a look at CODE/400.


Summary

You will find the opportunity for reuse much higher with ILE COBOL. Many customers have found it an effective way to standardize on single, highly optimized and highly tested versions of previously heavily repeated functions throughout their applications.

As you have seen throughout this book, Java and all object-oriented languages emphasize reuse. By nicely partitioning your COBOL code into ILE modules, you will be in a better position to introduce Java into your existing application, reuse these COBOL modules in all-new Java applications, or use them from new client user interfaces such as a GUI or a Web browser.


Appendix C: Object-Oriented COBOL On OS/390

Overview

IBM supports object-oriented (OO) COBOL on OS/390 (as well as Windows, OS/2, and AIX). This is not available on OS/400. Object-oriented COBOL is an extension of COBOL to support the following concepts:

  • Classes and methods.

  • Instantiating (allocating) objects from classes.

  • Inheritance and method overriding.

While most OS/390 COBOL programmers are not using the object-oriented capabilities in IBM COBOL for OS/390 and VM, you might benefit from a cursory introduction to the syntax and concepts to help you understand the universal OO concepts that apply to all OO languages, including Java. That is, by seeing how these concepts are implemented in COBOL, you will be better able to understand them in Java. Who knows, you might even decide to use OO COBOL! And, if you already know and use OO COBOL, this review and comparison to Java will really jump-start your Java learning experience.


Java Object Model Review

If you are reading this appendix prior to reading chapter 9, you need a quick overview of the notion of inheritance, so you can properly compare OO COBOL to Java. In Java, you can define classes by using the syntax for classes, as follows:

public class Order
{
   // instance and static variables
   // method definitions
}

You can also create a new class that extends an existing class by using the extends keyword in the class definition:

public class SpecialOrder extends Order
{
   // new instance and static variables
   // new methods and overrides of inherited methods
}

This defines a new class (SpecialOrder, in this case) that inherits all the non-private variables and methods of the parent class it extends (Order, in this case). You can add new methods to extend the functionality of the parent class, or redefine a method from the parent class. This is how you get inheritance in Java. Note that you can only inherit from one class (unlike OO COBOL), although you can implement one or more interfaces, which are special types of classes discussed in chapter 9.

To override a method from a parent's class, you simply define it again in the child class and supply new code for its body. Its signature (name, number of parameters, and type of the parameters) must exactly match that of the parent version. Its return type must also match. When writing the new code, you might want to call the code in the parent's method. This can be done by using super.methodname(…), that is, you can call the parent's version of any method by preceding the method name with super. If you don't do this, Java will look first in the current child class for the referenced method.

When you create base classes in Java that do not use the extends keyword, you are still actually extending a class. All base classes implicitly extend the Java-supplied class called Object in the java.lang package, which is always implicitly imported for you. Thus, Object is the ultimate parent (either directly or indirectly) of all classes in Java. You will see that all classes in OO COBOL also have an ultimate parent, but for base classes it is not implicit, it is explicit. That means you must explicitly state that your base class extends this system-supplied root class.

Further, every class currently in use in a running Java application also has an instance of the Class class (again from java.lang). This instance is automatically created by the Java runtime at application startup, for all classes used by the application. It is responsible for actually allocating instances of your class when new is used, and it also has lots of interesting information about the class itself, such as all the method names and signatures, all the instance and class variable names and types, and inheritance information. This information can be queried by your code, if desired.

This type of "overseer" class for classes is called a meta class. Object-oriented COBOL also has a system-supplied meta class. If fact, in OO COBOL, you can actually create your own meta classes, while in Java you cannot. However, this is a rarely used freedom.

The rules for inheritance, super parent classes, and meta classes are referred to collectively as an object model. All OO languages have an object model dictating similar rules and system-supplied class names. Figure C.1 summarizes Java's object model.

Click To expand
Figure C.1: Java's object model.


Overview Of OO COBOL

To use objects in COBOL, you define classes, which are very similar to normal COBOL programs, except they contain methods instead of paragraphs in their PROCEDURE DIVISION. The methods themselves look like nested programs. They can have all four DIVISIONs, including a LINKAGE SECTION for defining parameters. Figure C.2 shows this high-level view.

Click To expand
Figure C.2: Anatomy of a COBOL class.

The classes you define can be base classes, or they can be defined to extend one or more parent classes. Note the terminology for inheritance, however, is inherit, versus extend. There is syntax as well for instantiating or allocating classes as objects and calling methods in these objects. This requires a new data type for holding references to instantiated objects, much like Java. You also must explicitly identify the classes you intend to reference or use, much like the import statement does in Java.

COBOL classes are compiled into object modules, then pre-linked and link-edited into DLL modules. Regular COBOL programs can use OO classes simply by using the syntax for instantiating objects and calling methods. This is necessary because DLLs cannot be called directly from the command line. Only executable load modules, which are as always comprised of compiled COBOL programs, not compiled COBOL classes, can be called from the command line. Compiled COBOL classes are only allowed inside DLLs.

Another interesting aspect of OO COBOL is that it is built upon the System Object Model or SOM, which is IBM's CORBA (Common Object Request Broker Architecture) implementation. This is an industry standard for writing classes and objects in a language-neutral way. It is defined by the Object Management Group, or OMG, of which IBM is a member.

The IBM SOM product is not the only IBM implementation of CORBA. It is also more recently implemented in the WebSphere Application Server product, Enterprise Edition. However, only SOM and OO COBOL enables you to write classes and objects in COBOL using native COBOL syntax.

SOM works by allowing you to define the class interface definitions in a pseudo-language called IDL (Interface Definition Language), and implement the actual executable code in any other language. These definitions are stored in an interface repository (IR) and APIs are supplied so that code in any other language can instantiate classes defined in the repository and call methods on them. You can even dynamically query the class names and method signatures of the classes in the repository.

In OO COBOL, you do not have to worry about IDL, however. You can and usually do just define your classes and methods using the syntax you are about to see. The compiler automatically puts the class definitions into the repository for others to use. You could write code in any other language that uses the SOM APIs to instantiate a COBOL object, and invoke methods on it.

Like Java, OO COBOL classes all directly or indirectly extend a system-supplied class, which for COBOL is SOMObject, a class supplied by SOM. If you do not extend any other class, you must extend this one.

Similarly, all classes in an application have an instance of a meta class created for them at runtime. This class, by default, is SOMClass, although there is syntax in OO COBOL (not described here) to define your own meta class, and then specify that meta class for new classes you define.

Figure C.3 summarizes OO COBOL's object model. Note that unlike Java, inheritance from multiple parents is allowed.

Click To expand
Figure C.3: OO COBOL's object model.


Defining Classes In OO COBOL

Defining a class in OO COBOL is similar to defining a program in COBOL, in that there are still the four DIVISIONs as you are used to. We won't go into great detail here, because we only want to give you an idea of how OO is implemented in COBOL, not teach you the syntax.

Identification Division

The IDENTIFICATION DIVISION is much like it is for a program. However, instead of a PROGRAM-ID paragraph, you have a CLASS-ID paragraph where you give the name of your class and identify what class or classes it extends, or in OO terms, inherits from. If you don't inherit from any other class, you must explicitly inherit from SOMObject:

Identification Division.
Class-Id. MyClass INHERITS SOMObject.

Environment Division

The ENVIRONMENT DIVISION is required for classes, and must have a REPOSITORY paragraph where all classes that your code references must be identified. You must also list here the class or classes that your class inherits from. Optionally, you can list this class itself, in which case you can use a class name internally that is different than the actual class name in the SOM interface repository:

Environment Division.
Configuration Section.
Repository.
   Class SOMObject is 'SOMObject'
   Class MyClass is 'MyClass'
   Class MyOtherClass is 'MyOtherClass'.

If you do not explicitly list this class in the REPOSITORY SECTION, its name is stored in up-percase in the SOM interface repository. This implies that any code using this class will need to refer to it in uppercase.

Data Division

The DATA DIVISION is optional for a class, but it is here that the instance variables are defined for the class, in the WORKING-STORAGE SECTION. This is the same syntax as for normal COBOL programs, except you cannot use the VALUE clause, or the EXTERNAL or GLOBAL attributes.

To initialize your instance variables, you must override the inherited method somInit and initialize them there. The somInit method is like a constructor in Java, in that if it exists, it is called by the runtime whenever an instance of the class is instantiated.

Procedure Division

The PROCEDURE DIVISION is optional for a class. However, it will rarely be omitted because it contains the actual method definitions for your class, the syntax for which you will see shortly. You must end your PROCEDURE DIVISION with the END CLASS statement, as follows:

End Class MyClass.

Note the required ending period.

Example

Listing C.1 is an example of an OO COBOL class, omitting the method definitions for now.

Listing C.1: An OO COBOL class.
Start example
Identification Division.
Class-ID.      MyClass Inherits SOMObject.
Environment Division.
Configuration Section.
Repository.
   Class SOMObject is 'SOMObject'
   Class MyClass is 'MyClass'
   Class MyOtherClass is 'MyOtherClass'.
Data Division.
Working-Storage Section.
01 price PIC 999V99.
01 discount PIC 99.
Procedure Division.
*** method definitions ***
End Class MyClass.
End example


Defining Methods In OO COBOL

Defining a method in OO COBOL is similar to defining a nested program in normal COBOL. Inside the PROCEDURE DIVISION, prior to the final END CLASS header, you insert what appear almost like entire embedded programs, in that they can have all four DIVISIONs.

Identification Division

The IDENTIFICATION DIVISION for a method is much like it is for a program or class. Instead of a PROGRAM-ID paragraph or CLASS-ID paragraph, however, you have a METHOD-ID paragraph that names your method and identifies whether or not it overrides a method of the same name in the parent class. Here is an example:

Identification Division.
Method-Id. MyMethod.

For overrides, it looks like this:

Identification Division.
Method-Id. MyMethod OVERRIDE.

Environment Division

The ENVIRONMENT DIVISION is optional. It is the same as for normal COBOL programs.

Data Division

The DATA DIVISION is also optional and the same as it is for normal COBOL programs. Note that the LOCAL-STORAGE SECTION is allowed. It defines data items whose memory is only allocated when the method is called, and deallocated at the end of the method. Thus, this is truly like local variables inside methods in Java.

Procedure Division

The PROCEDURE DIVISION is optional for a method. However, it will rarely be omitted because it contains the actual executable code for your method, just as you would write it for a program. That is, it contains sections, paragraphs, and the like. All methods must end with the END METHOD statement, as follows:

End Method MyMethod.

Example

Listing C.2 is an example of an OO COBOL class, including the method definitions.

Listing C.2: An OO COBOL class with method definitions.
Start example
Identification Division.
Class-ID.      MyClass Inherits SOMObject.
Environment Division.
Configuration Section.
Repository.
   Class SOMObject is 'SOMObject'
   Class MyClass is 'MyClass'
   Class MyOtherClass is 'MyOtherClass'.
Data Division.
Working-Storage Section.
01 price PIC 999V99.
01 discount PIC 99.
Procedure Division.

Identification Division.
Method-ID.     myMethod.
Procedure Division.
Display "Hello World".
End Method myMethod.

End Class MyClass.
End example


Using Classes In OO COBOL

Once classes are defined in OO COBOL, you can put them to use from other classes or from traditional COBOL programs. Like Java, and unlike normal COBOL, you cannot simply CALL these classes. Rather, you must allocate instances (objects). Then, and only then, can you invoke the methods on that instance.

To prepare to create an instance of a class (an object), you must declare an object reference variable (just like Java). You do this by defining a WORKING-STORAGE data item that specifies the new object reference usage, as follows:

01 myObject Usage Object Reference MyClass.

The identifier name is specified as usual, along with the new usage phrase OBJECT REFERENCE. This is followed by the name of the class that this object reference will eventually refer to. Like Java, the actual instantiation is another step.

You can also omit the class name, creating an object reference variable that can refer to objects of any class, as follows:

01 anyObject Usage Object Reference.

This is identical to declaring an object reference variable in Java whose class type is Object, which is the ultimate parent of all classes. Remember that the classes you name in the object reference definitions must be identified in the REPOSITORY paragraph of the CONFIGURATION SECTION.

Instantiating OO COBOL Objects

Once you have defined an object reference variable in the coding part (PROCEDURE DIVISION) of your method, you can and must instantiate an object and assign the resulting reference to your object reference variable. In Java, this is done with the new operator. In COBOL, you must use a method from the base SOMObject class that all classes have as the ultimate parent. This method is called somNew. To call it (or any method), use the new INVOKE statement:

Invoke myClass 'somNew' Returning myObject.

The syntax here is class name, followed by method name, followed by the RETURNING phrase for methods that return something, followed by the name of a data item to contain the returned value:

  • Class name is the class that contains the method to be called, which is the previously defined class that you want to instantiate.

  • Method name is the method to be invoked. (Always use the inherited method somNew for instantation.)

  • RETURNING phrase is only for invocation of methods that return something.

  • Name of a data item is a data item to contain the returned value. (The somNew method returns a reference to a newly allocated instance of the named class, which is assigned to the object reference variable from the WORKING-STORAGE SECTION.)

The class name you specify when using somNew must be the same as the one specified in the WORKING-STORAGE declaration of the object reference variable specified in the same somNew invocation. Well, either exactly the same or a child class. Again, this is just like Java, which also allows a child class to be specified with the new operator.

Note that prior to instantiating, object reference variables point to null, just as they do in Java. In COBOL, you can use either the NULL or NULLS figurative constant to test for this (in an IF statement) or assign to this (in a SET statement). Also note that when you compare two object reference variables, you are comparing the contents of their memory addresses, just as in Java.

OO COBOL predefines a special object reference variable for you, called SELF. This always holds a reference to the current object your code is running in. Thus, it is identical to the this special keyword in Java.

Invoking OO COBOL Methods

To invoke methods on allocated objects, use the INVOKE statement. This requires that you have already instantiated an object and it is referenced by your object reference variable. For example, in the running example, MyClass has been instantiated and assigned the reference to object reference variable MYOBJECT. To call MYMETHOD1 in that object, which takes a numeric value and returns a numeric value, you would code this:

Invoke myObject 'myMethod1' Using myInput Returning myResult.

The data items MYINPUT and MYRESULT are assumed to be numeric data items for this example. To call MYMETHOD2 taking no parameters and returning nothing, code this:

Invoke myObject 'myMethod2'.

Note the use of quotes around the method name. This is for hard-coded method names. You could also put the method name in an alphanumeric data item containing the name of the method. In this case, though, the object reference variable must be defined without a class name in its declaration. Java has no such capability-all method names must be literals when they are called.

The INVOKE statement supports the ON EXCEPTION and NOT ON EXCEPTION phrases, just like the CALL statement does. This is similar to putting the method call inside a try block in Java, and using appropriate catch blocks to deal with exceptions, as discussed in chapter 10.

Freeing or Deallocating OO COBOL Objects

OO COBOL, unlike Java (but like C++), requires you to look after freeing up an object when you are done with it. To do this, invoke the inherited method somFree as follows:

Invoke myObject 'somFree'.

If you don't take care to free up unused objects, you will be wasting memory as your program executes, a nasty thing called memory leakage. Note how nice it is that Java takes care of this high-maintenance headache by running its garbage collector in the background throughout the life of a Java application.


Summary Comparison to Java

Table C.1 summarizes the comparisons of OO COBOL to Java.

Table C.1: Comparing Object Models Between OO COBOL and Java.
OO COBOL | Java
All classes inherit from SOMObject. | All classes inherit from Object.
The default meta class is SOMClass, but you can create your own meta class. | The meta class is Class. You cannot create your own.
A child class can inherit from multiple parent classes. | A child class can inherit from only one parent class, but multiple Interfaces (as discussed in chapter 9).
The REPOSITORY paragraph of the ENVIRONMENT DIVISION identifies all classes referenced or inherited from. | Import statements identify packages containing classes referenced or extended (only needed for classes not in this package, though).
Object reference variables. | Object reference variables.
Instantiate using the supplied somNew method. | Instantiate using the new operator.
You must explicitly free objects. | Java looks after freeing objects.
Use SELF to refer to this object. | Use this to refer to this object.
Use SUPER to refer to the parent. | Use super to refer to the parent.
The SOM repository identifies classes at compile time and runtime. | The CLASSPATH environment variable identifies classes at compile time and runtime.


Appendix D: Using Java On OS/390

Overview

This appendix provides a brief tour of getting, installing, and using Java on OS/390. Although you might start your Java journey by learning Java while on your personal

computer, OS/390 is committed to being a first-class Java platform based on its own merits. That means you can run Java applications on OS/390 itself.

Because Java is so portable, there are not too many special instructions for doing this, but there is some additional information that will give you a leg up. Using Java on OS/390 involves at least a passing knowledge of the following:

  • Java for OS/390. The JDK, including the interpreter and all packages.

  • OS/390 UNIX System Services Shell. The UNIX shell required for working with the JDK commands.

  • VisualAge for Java. IBM's premier development product for Java. As an OS/390 programmer, you will want the VisualAge for Java Enterprise Edition for OS/390, which includes the host components and the client VAJ Enterprise Edition (which includes the Enterprise Toolkit for OS/390).

  • High Performance Java (HPJ). For compiling Java applications into OS/390 executables (dynamic linked libraries, or DLLs) that are much faster than interpreted Java applications. This is actually shipped as part of VisualAge for Java's Enterprise Toolkit for OS/390.

  • CICS Transaction Gateway. For writing applications that access existing CICS transactions, using either EPI or ECI. It includes Java client APIs for accessing the gateway via TCP/IP (formerly known as the CICS Gateway for Java), and the JCICS library. The Gateway runs on either a remote client or the local OS/390. If the latter, only ECI is available, not EPI.

  • JCICS. A Java package for writing new CICS transactions in Java or extending existing COBOL transactions in Java. JCICS is available in VisualAge for Java and in the CICS Transaction Gateway.

  • IMS TCP/IP OTMA Connection (TOC). A gateway for accessing IMS applications that supports TCP/IP socket connections from any client. It uses XCF to talk to OTMA.

  • IMS TOC Connector for Java. The Java interface to the IMS TOC, allowing you to easily access existing IMS transactions from Java. The Java code can be local to the server or remote on a client. It is available in VisualAge for Java.

  • Java Record I/O (JRIO). A set of Java classes for accessing sequential and keyed data sets via traditional COBOL-style record access.

Each of these is covered in a bit more detail in this appendix. If you decide to use Java Servlets and JavaServer Pages (JSPs), you need to read about the WebSphere Application Server and its choice of Web servers: Domino Go Webserver or the newer IBM HTTP Server (based on the popular open-source APACHE server). As of V2 R6, everything you need to efficiently run Servlets and JSPs is built into OS/390.

Enterprise Java Beans (EJBs) are another topic you should read about for writing robust, scalable, server-side Java components. The effort behind the EJB specification was to provide a platform-independent interface that exposes the qualities of service available from individual servers in a uniform way. As such, OS/390 delivers two compatible, but different, implementations of the EJB specification. One is based on the Component Broker implementation on OS/390. The other will be made available in a future release of the CICS Transaction Server. Both of these implementations benefit from and exploit the OS/390 qualities of service, and both implementations will be available to WebSphere-based applications.

Chapter 14 touches on Servlets, JSPs, and Enterprise Java Beans, but we leave the details for other books. If you are interested in these topics, you can start your research at www.ibm.com/websphere.


Installing The Components

Table D.1 provides information for installing the various pieces, at a Version 2 level. Note that versions of the operating system after V2 will possibly have a number other than 5655.

Table D.1: Installation and Ordering Information.
Product | Product Number | Notes
Java for OS/390 (Java Development Kit) | 5655-A46. Feature 5801 for 6250 tape, 5802 for 3480 cartridge, or 5700 for 4mm. | You can also download it from www.ibm.com/s390/java. Version 1.1 runs on all OS/39 versions and releases, but has successively better performance with later versions and releases. Version 1.2 might only run on later versions and releases.
UNIX System Services |  | Formerly known as Open Edition.
VisualAge for Java Enterprise Edition for OS/390, including HPJ | 5655-JAV | See www.ibm.com/software/ad/vajava. You need the client software, the OS/390 runtime (free), and the OS/390 compiler.
CICS Transaction Gateway |  | Available from the CICS Web site, with VisualAge for Java Enterprise Edition (client version only), and with CICS Transaction Server 1.3 or higher.
JCICS |  | Available with VisualAge for Java, Enterprise Edition, and with CICS Transaction Gateway.
IMS TOC |  | Download for free from www.ibm.com/software/data/ims/imstoc.html.
JRIO |  | Part of Java for OS/390, or download from www.ibm.com/s390/java.

All the intricate details of ordering, installing, configuring, and using these products are covered in great detail in two redbooks: Integrating Java with Existing Data and Applications on OS/390 (SG24-5142) and e-business Application Solutions on OS/390 Using Java: Volume 1 (SG24-5342). The following redbooks are also recommended reading:

  • Getting Started with Component Broker for OS/390 (SG24-5601).

  • Java Programming Guide for OS/390 (SG24-5619).

  • OS/390 e-business Infrastructure; WebSphere Application Server 1.1 for OS/390 (SG24-5604).

  • Java Application Development for CICS: Base Services and CORBA Client Support (SG24-5275).

They are all available online (in PDF format) from www.ibm.com/redbooks.


The OS/390 Java Development Kit

As mentioned in chapter 1, each release of JDK is written in its original form for Sun Solaris and Microsoft Windows operating systems by Sun Microsystems. IBM licenses this, and the IBM Hursley laboratory in England ports it to all operating systems.

The JDK is a standard, and by the terms of the licensing agreement, IBM and others can only add to it, not take away from it. So, you will find that all the standard JDK commands are available on OS/390. These include java, javac, javap, javadoc, and jar, among others. Of course, these are not the standard OS/390-style command names that you are used to. To make OS/390 Java support as an industry standard as much as possible requires that the JDK be based not on the MVS data set file system, but rather on the Hierarchical File System. This is true of Java source files, Java class files, Java archive files (.jar), and all the JDK commands.

To get the OS/390 JDK, you can either order from IBM, or download it from the Internet to your workstation and then FTP it to your OS/390 system (to the HFS). At the time of this writing, it supports JDK level 1.1.8. Once you have the files on your system, use the supplied REXX script ajvinst to install it. Your system programmer also needs to update your /etc/profile HFS file to add the binary directory for Java for OS/390 to your path. For current details, see the instructions available from www.ibm.com/s390/java.

Using the Hierarchical File System and IShell Interpreter

COBOL development uses the native MVS data set file system, which typically involves data sets of the naming convention userID.xxxx(membername). The underlying collating sequence is EBCDIC, which is different from the ASCII sequence used by other operating systems (except for OS/400, which is also EBCDIC-based).

The HFS is an alternative to the MVS data set file system. It involves directories that can contain other directories (subdirectories) or stream files, much like Windows. Stream files are simply flat files that contain a sequence of bytes, with no record partitioning and no relational database constructs, again, just like files on Windows. The highest-level directory is called the root. It is designated by a single forward slash, /. Unlike Windows, the HFS uses a forward slash for path names, not a backward slash. So, if you have a file called HelloWorld.class in directory myJava, which in turn is in directory Phil, the fully qualified path name of the file is

/Phil/myJava/HelloWorld.class

Note that directory and file names are case-sensitive in the HFS. If a given path name does not start with a forward slash, then it is relative to the current directory. The HFS is also an EBCDIC-based file system.

To work with the HFS, you use commands that are specific to it. These commands are typically issued after entering the OS/390 UNIX System Services shell environment that sits on top of the HFS, but can also be entered from TSO/E by prefixing them with the TSO/E command OSHELL. Further, you can use one of two remote access options: rlogin and telnet.

In all cases, your system programmer must first make you an OS/390 shell user by assigning you an OMVS user ID (UID) and group ID (GID). The former is the unique ID used for private ownership and privileges, while the latter is a group mechanism for privileges. The system programmer will also create a home directory for you, which is typically the same name as your user ID and is located under the /u directory (for "users"). When you enter the shell, you will be in this directory initially. Further, the environment variable $HOME will be set to this for your user ID.

Once you have a user ID and directory, you can use the OS/390 UNIX System Services shell by typing the command OMVS from a TSO/E prompt. This puts you in a command shell where you can enter standard UNIX commands instead of TSO/E-style commands. This will be more comfortable if you are familiar with UNIX. It is also where you enter JDK commands, but we'll get to that later in this appendix.

Table D.2 lists some of the common commands you might use in Windows, and their equivalent shell commands. This table should be enough to get you going with the shell. More detailed information is available at www.ibm.com/s390/unix, and the manuals UNIX System Services User's Guide (SC28-1891) and UNIX System Services Command Reference (SC28-1892).

Table D.2: Windows Versus Ishell Commands.
Command | Windows | OMVS Shell
Show the current directory name. | cd (no parameters) | pwd
Change the current directory. | cd path or \path | cd path or /path
Make a directory. | md or mkdir | mkdir
Delete a directory. | rd or rmdir | rmdir
Rename a directory. | rename | mv
Display or change the directory owner. | explorer | chown
Display or change the directory authority. | attrib | chgrp, chmod
List the files in a directory. | dir | ls
Display a file's contents. | type | pr, cat, tail, head
Copy a file. | copy | cp
Move a file. | move | mv
Delete a file. | erase | rm
Rename a file. | rename | mv
Display or change the file owner. | explorer | chown
Display or change the file authority. | attrib | chgrp, chmod
Create a link. |  | ln
Define an alias (shortcut) command. |  | alias
Exit the shell or prompt. | exit | exit
Run a TSO/E command. |  | tso
Search files for a string. |  | grep
Set an environment variable. | set | export
List previously entered commands. |  | history
Compress/uncompress files. | winzip or jar | tar/untar or jar
Get online help for a command. | help | Commandman

In addition, there are commands for copying files between the HFS and MVS data sets. These are the TSO/E commands: OPUT to copy from MVS to HFS, OGET/OGETX to copy from HFS to MVS, or OCOPY to go either way. There are even OEDIT and OBROWSE commands to edit or browse HFS files with ISPF directly from TSO/E.

Working with Java on OS/390

Now that some basics are out of the way, it is time to focus on working with Java. To run a Java class on OS/390, you enter the OMVS shell interpreter, ensure you are in the directory containing the Java class file, and then run it as usual using the java command. It is that simple. All of the JDK commands are there, and they are identical to their Windows counterparts-well, actually, their UNIX counterparts, but other than remembering to use a forward slash (/) instead of a backslash (\) for path names, it is the same.

Your first order of business is to get a Java class to run. You have two choices to do that: either create the .class file using javac on Windows (say) and copy that to OS/390 HFS, or copy the .java source file from Windows (say) and use javac on OS/390 to create it. In either case, Java development is usually done on a workstation running Windows (say) and Java development tools like VisualAge for Java. However, you could use ISPF to edit a source member containing Java, and then use OCOPY to copy that member to an HFS file. You could also use ISPF directly against your HFS Java file (OEDIT), or use one of the shell-supplied editors like vi. However, Java is an exciting new language, so why not use exciting new tools for it? You have several options to get your .java or .class files from Windows to OS/390 HFS. A few of the most common are listed in Table D.3. Pick whatever you like, or whatever is handy.

Table D.3: Options for Copying Java Files to OS/390 HFS.
Option | Description
Use FTP (File Transfer Protocol). | If your TCP/IP configuration allows this, entering ftp from the Windows command line is an easy way to copy any file to OS/390.
Use an NFS mounted file system. | If you have a product like Hummingbirds, you can mount an OS/390 drive to your Windows system, and copy the files.
Use OS/390 SMB support. | OS/390 Version 2, Release 8 and later has an implementation of Windows SMB. This allows NET USE on Windows to mount your OS/390 file system.
Use VisualAge for COBOL. | If you use VisualAge for COBOL for your COBOL programming (and you should), it has support for exporting files from Windows to OS/390.
Use VisualAge for Java. | The Enterprise Toolkit for OS/390 in the Enterprise Edition has easy-to-use support for exporting your Java source or class files to the HFS, directly from the IDE. As of this writing, it requires a product like Hummingbirds for an NFS mount, but that is not expected to be the case for long. You only specify the target location once, and it remembers it every time.

The only one of the options in Table D.3 that you might need help with is FTP. Here is a sample script from a Windows DOS prompt window:

c:\Phil\myJava>ftp MYSYSTEM
User: coulthar
Password:
230 COULTHAR logged on.
ftp> cd /Phil/myJava
250 "/Phil/myJava" is current directory.
ftp> binary
200 Representation type is binary IMAGE.
ftp> put HelloWorld.class
467 bytes sent in 0.02 seconds
ftp> ascii
200 Representation type is ASCII
ftp> put HelloWorld.java
206 bytes sent in 0.03 seconds
ftp> quit

Setting CLASSPATH in OMVS Shell

Now your Java files are in the HFS, and you know how to find your way to them and run them using the UNIX shell. You are on your way. The last thing you really need to know is how to set your CLASSPATH so that Java can find your Java classes. By default, it only looks in the current directory, but that is not very useful. You will often have classes spread over multiple directories.

The first important note about the CLASSPATH environment variable on OS/390 is that it is based on UNIX, so it uses a colon to separate the entries, not a semicolon as in Windows:

/Phil/myJava:/George/myJava

To set the CLASSPATH dynamically, that is, each time you run Java, use the -classpath parameter to the java command. You must be careful to include the classes.zip file from OS/390 for Java (the JDK), as shown here (split over multiple lines due to space constraints):

   java -classpath
/Phil/myJava:/George/myJava:/usr/lpp/java/J1.1/lib/classes.zip
HelloWorld

This is nice, but it is a pain to remember each time. To make it more permanent, you can set it once for your shell session using the export command, much like the SET command in Windows. Here is an example (excluding the classes.zip entry, for brevity):

export CLASSPATH=/Phil/myJava:/George/myJava

If you type this in your UNIX shell session, that CLASSPATH remains in effect for the duration of the session. To see all the current environment-variable settings, type export with no parameters.

This is a little better, but only marginally. To get more permanence, create a file called .profile ("dot profile") in your home directory in the HFS and put an export command in it, as shown here (again, classes.zip is omitted for brevity):

export CLASSPATH=.:/Phil/myJava:/George/myJava

Use the same options discussed for .java files to create your .profile file and copy it to the appropriate HFS directory. This is an example where ISPF and OCOPY might be just fine.

Note that the example included a period, which represents the current directory. We recommend always putting this on your CLASSPATH, even in Windows. For this .profile to work correctly, it must be in your home directory, as specified in the $HOME variable for your shell user profile. By convention, your home directory should be /u/userID, where userID is your user ID name.

You will probably most often use the .profile option for your own personal Java exploration. However, eventually you or your system programmer might install a Java application for general use on your system, and the time will come to set the CLASSPATH globally for all users. To this, place the .profile file in a special directory: /etc/profile. In there, it will be applied to all users on the system.

The .profile file probably already exists in this directory, so you will want to append to it instead of creating it. To do this, use OEDIT from TSO/E to edit it in place with ISPF. When running a Java program, the CLASSPATH specified in /etc/profile/.profile is checked first when looking for Java classes, and the CLASSPATH specified in your home directory .profile is searched next if the class is not found. Thus, these two files are cumulative, not mutually exclusive. The /etc/profile file should only be updated by your system programmer due to its pervasive impact.

Java in Batch

One common question that comes up is how to run the JVM in batch to compile or for Java execution. You can deduce how to do both of these from the example in Listing D.1. The shell environment needs to be able to find the javac program. Thus, the shell needs to use the correct path, CLASSPATH, and other environment information, as already described. (Thanks to Mark Cathcart for this example.)

Listing D.1: Using Java in batch.
Start example
//JAVAC    JOB (D03,07CS),'JAVAC COMPILE',
//     GROUP=P542,MSGCLASS=D,REGION=1600M TYPRUN=HOLD
/*JOBPARM  T=99,L=99999,C=9999999
//*********************************************************************
//*********************************************************************
//TEST      EXEC PGM=BPXBATCH,
// PARM='sh javac /u/p542/java/HelloWorld/HelloWorld.java'
//SYSPRINT  DD  SYSOUT=*
//STDOUT    DD  PATH='/tmp/p542ivp.out',
//          PATHOPTS=(OWRONLY,OCREAT,OTRUNC),PATHMODE=SIRWXU
//STDERR    DD  PATH='/tmp/p542ivp.err',
//          PATHOPTS=(OWRONLY,OCREAT,OTRUNC),PATHMODE=SIRWXU
//*
End example

Java Remote AWT

Java for OS/390 runs as a separate process in a separate address space. You can also run it as a batch job by using BPXBATCH, or from the command line. It does not have support for 3270 screens or maps of any kind, and is intended for non-interactive functions. If your application requires a user interface, it is expected that you will either use a Web-based interface via Servlets and JSPs, or you will write a separate Java GUI that runs on the client and remotely connects to your Java server code. This could be through any middleware technology such as sockets, RMI, CORBA, MQSeries, or the CICS or IMS gateways. However, there is some support for running Java GUI applications directly on OS/390. The two options are Remote AWT and X/Windows.

Remote AWT (RAWT) allows you to remotely (and automatically) distribute the GUI API calls (that is, the AWT method calls) to a GUI-capable client. It requires that you be running from a GUI-capable client (Windows or Network Station), using, say, an emulator window to start the Java application. Another option is to use X/Windows emulation on an X/Windows client. Remote AWT performs better than X/Windows and has fewer configuration requirements for the clients. It provides a way for end users on workstations that are Java-enabled to interact with their applications. Although both options are relatively slow and not very scalable in terms of concurrent users, they do have some value, such as for installation and administration user interfaces.

For multiple-user interfaces, we feel it is better to explicitly split your applications between client and server. Your server code can then exploit technology such as threads and message queues to efficiently handle multiple users simultaneously. For Web-based user interfaces, you also get the built-in scalability functions in the IBM HTTP Server, such as load balancing. However, remote AWT might be a useful way to build a monitor into Server components for an application or systems administrator to use. Since it is completely compatible with the original AWT implementation, you can use it on NT and UNIX servers with a local display, and then on OS/390 with a remote display.

As of this writing, there is no Remote Swing technology, but it is expected soon. Word has it that it is a significant improvement over Remote AWT, so you might want to check it out. Look for the name Jbroker, on www.ibm.com/alphaworks.


Of Gateways, Connectors, Packages, And More

OK, you are now a Java person. Congratulations. You have been through an introduction to writing pure Java code for Windows and OS/390, and you can access existing DB2 data with JDBC or SQLJ. However, this might not quite be enough. You might need to also access the following from Java:

  • CICS.

  • IMS.

  • SAM files.

There are ways to do this, but they are not part of the industry-standard JDK, since they are unique requirements for IBM customers. Instead, they involve unique code written by IBM to allow access to these important assets. Numerous manuals, redbooks, and whitepapers cover these topics, so we will just give you a high-level overview of what the pieces are and where they fit.

The following pieces are necessary to get to your subsystems:

  • Gateways that are provided by the subsystem teams themselves. There are usually gateways that run on clients remotely connected to the subsystems, and gateways that run on the same OS/390 server system as the subsystem itself. Think of the gateways as the runtimes you need. Examples are the CICS Transaction Gateway and the IMS TOC.

  • Java client APIs to access the gateways specifically from Java. Often, the subsystem supplies unique classes to access the gateway from Java, and these are included with the gateway or downloadable from the Internet. Sometimes this code only works from Java running on the server, and sometimes it works with code running either on the server or on a remote client. Examples are the CICS Transaction Gateway's Java gateway application and the IMS TOC Connector for Java.

  • Common Connectors. This is an attempt by VisualAge for Java to provide a single, common, standard interface for writing Java code to any of the underlying subsystems or products. This is a layer of abstraction, and allows you to write similar-style code to access all kinds of underlying subsystems. Sometimes these go directly to the underlying gateways, and sometimes they rely (behind the scenes) on the gateway's specific Java client APIs. They are usually a combination of developer tools that generate code uniquely for your application, and the runtimes the generated code needs, in addition to the gateway itself. The developer tools are all part of IBM's VisualAge for Java, Enterprise Edition, and are collectively known as Enterprise Access Builders. These builders all generate code that sits on top of a framework called the Common Connector Framework. Sometimes the runtimes needed by the output of these tools are made available by the subsystems themselves and with VisualAge for Java.

For a complete discussion of all the connector and gateway options and their relative positioning, refer to the whitepaper OS/390 Guide to e-business Connectors at www.ibm.com/s390/nc/connectors/.

Common Connector Framework (CCF)

Table D.4 details the connectors included with the CCF feature of VisualAge for Java at the time of this writing. The CCF Connectors are shown graphically in Figure D.1.

Click To expand
Figure D.1: CCF Connectors in VisualAge for Java.
Table D.4: CCF Connectors in VisualAge for Java.
Connector | Description
CICS Connector | Sits on top of the CICS Transaction Gateway, enabling TCP/IP access to CICS transactions. When the gateway runs on a client, it supports ECI and EPI. When run on OS/390 itself, it supports only ECI. Code can run remotely on a client, remotely on another server, or on the same server.
Encina DE-Light Connector | Wraps the DE-Light Java client with a CCF conformant interface, for DCE applications.
Host On Demand (HOD) Connector | Wraps the eNetwork HOD Java client with a CCF conformant interface, for access to 3270 data streams from a remote client.
IMS TCP/IP OTMA (TOC) Connector | Sits on top of the IMS TOC, adding a CCF conformant interface for access to existing IMS transactions from a remote client, remote server, or the same server.
MQSeries Connector | Wraps the MQSeries Client Class for Java with a CCF conformant interface, allowing a Java application or applet to connect to an MQSeries server via TCP/IP.
SAP R/3 Connector | For accessing existing SAP R/3 implementations locally from Java.

Java client code you write to access a gateway or connection via a connector looks very similar for all the connector types. You start by creating a ConnectionSpec object that defines all the necessary information to access the back-end resource, such as the hostname and port number. From the ConnectionSpec object, you call a factory method to create an InteractionSpec object, which defines an explicit interaction, such as the name of a program to call. You pass the InteractionSpec object to a Communication object to actually communicate with the explicit resource identified in InteractionSpec. You must also pass the input and output records, which are described shortly.

The method you call inside the Communication object is execute, which is defined as execute(InteractionSpec, Object, Object). Two other important methods are connect and disconnect. As mentioned, the two Object parameters are input and output records. These are just byte arrays containing the data for all the fields to be passed, and to contain all the data of the fields to be returned. To help create them, there is another package of useful classes and interfaces called com.ibm.record. This package is known as the IBM Java Record Library.

Typically, your input and output buffer objects will be of a class type that extends either CustomRecord or DynamicRecord. In both cases, the class supplies get and set methods for reading field data from the buffer and setting field data into the buffer. The attributes of those fields will be predefined by another helper class that implements the IRecordType interface. This holds all the field definitions for the record, including attributes such as field name, length, type, and so on. CustomRecord class extensions are used when the field positions within the buffer are well-known and unchanging, and can be hard-coded. DynamicRecord class extensions are used when the fields might be added or removed on-the-fly, in which case the field positions within the buffer have to be computed with each get and set call.

Each connector might supply its own classes for the record objects (like CICS), or it might simply use the classes in com.ibm.record as-is (like IMS). In either case, there is tooling in VisualAge that shields you from much of the details.

Each of the XXXSpec objects is really an instance of a class that implements an interface from the package com.ibm.connector, implemented uniquely for each connector (by the connector developers, not you). Thus, the actual names for the classes vary by connector. For example, for the IMS connector, they are IMSConnectionSpec, IMSInteractionSpec, and IMSCommunication, all of which are in package com.ibm.connector.imstoc. For CICS, they are CICSConnectionSpec, ECIInteractionSpec, EPIInteractionSpec, and CICSCommunication, all of which are in the package com.ibm.connector.cics.

You can code to these directly if you wish, but it is much easier to use the Enterprise Access Builder tools that come with VisualAge for Java, and generate the code to use these classes for you.

Enterprise Access Builders

So what is the benefit of layering a common interface on top of many disparate Java gateways, accessing different existing resources? Well, one benefit is that it reduces your learning curve if you have to program to more than one of them. It is also a potential benefit to your company in the future, if you decide to switch from one resource to another. For example, if you decide to use an intermediate message queue instead of directly accessing CICS transactions, your Java code is only slightly affected, to the extent that you have to change from one connector to another. Another benefit, for you and for tool providers like IBM's VisualAge for Java, is that powerful yet generic tools can be written that work with the interface. That tooling will "magically" work with all the connectors currently available, and any new ones added in the future. (By the way, anyone can create new connectors.)

Let's look at some of that tooling. Inside VisualAge for Java, you will see something perhaps confusing at first glance:

  • Enterprise Access Builders for XXX. XXX includes Data Access, SAP, Java to C++, RMI, Domino, and Persistance.

  • Enterprise Access Builder. Also known as Enterprise Access Builder for Transactions.

What is the difference? The first is a series of SmartGuides (wizards), which ask questions and generate Java code to access specific resources, such as JDBC data, SAP functionality, C++ code, Remote Method Invocation objects, or relational data via Enterprise Java Beans. These do not use CCF connectors. The latter is a generic set of tools that work with connectors. Here is what these connector tools allow you to create:

  • Records. These are buffers or data structures. A record type is a buffer description, while a record is an actual buffer associated with a buffer description. Record types contain one or more field types, which define the attributes of a field, such as name, length, and type. Records contain one or more fields, which occupy specific locations within the buffer (byte stream). There are import tools to create a record type by importing from COBOL copy books, BMS or MFS maps, or 3270 terminal screen source files. There is an editing tool to create a record type from scratch or modify an existing one. Finally, there is a tool to create an actual record (capable of holding data) from a record type.

    A record contains methods for setting field data and retrieving field data. The record buffer itself is kept in a "network-ready" byte array format, internally. This buffer is what is sent across the network and back again for you. If you are running on the same system as the target gateway or connection, then it is a local communication. All codepage mappings are handled for you.

  • Mappers. These are Java objects generated for you to map the fields from a record object to the variables in a particular, specified Java object, and vice versa. There is a tool supplied to allow you to specify the field-to-variable mappings, which will generate the mapping logic for you. This tool can work with any two Java objects, not just records.

  • Commands. These are calls, via a connector, to one of the resources you want to access, for example, CICS, IMS, MQ, HOD, SAP, or Encina. Generically, these back-end systems are known as Enterprise Information Systems (EIS). Calling an EIS typically involves specifying the input buffer and output buffer (records) and the attributes of the actual connector connection. The Create Command tool will prompt for these and generate a Java bean that encapsulates all of this. The important methods of the generated bean are set XXX, execute, and get XXX. The XXX refers to the field names comprising the input record and output record, respectively. If your connector call might result in one of multiple possible output buffers, this tool can handle that, too. The bean fires an event for successful execution and another for unsuccessful execution.

  • Navigators. Often, to do something useful requires more than a single call to an EIS. Navigators are Java beans that contain multiple commands. Using the VisualAge for Java Visual Composition Editor, you can string together multiple commands by linking outputs to inputs.

  • Business Objects. These are like (and designed for use in) EJB entity beans. These are bean instances that can be uniquely identified by a particular key value. To support this, there are two classes per EAB BO: the class itself and a related Key class that defines the attributes (usually a subset of the classes attributes) which comprise the key. Given a populated key object, a particular class object can be retrieved.

To use the Common Connector Framework and the Enterprise Access Builder tools, you must import the appropriate features in VisualAge for Java (using File®Quick Start®Add Feature in the WorkBench window):

  • Enterprise Access Builder Library.

  • IBM Common Connector Framework.

  • IBM Java Record Library.

  • The desired EIS Connector feature, such as Connector IMS TOC or Connector CICS.

Once you have these installed, use Selected®Add®Project and then Selected®Add®Package menu items to create first a project and then a package within the project. Finally, right-click on your package, and select Tools®Enterprise Access Builder to see the tools, as shown in Figure D.2.

Click To expand
Figure D.2: The EAB tools menu.

To create a command bean, follow these steps:

  1. Create input and output records using either the import tools or the Create Record Type and Create Record from Record Type tools.

  2. Create a Command Bean using the Create Command tool.

Once you have a command bean, you can use VisualAge for Java to write a Java application, applet, bean, Enterprise Access bean, or Servlet that uses the bean. You can also use the WebSphere Studio tools to generate a Servlet and input/output JSPs from that bean.

Figure D.3 shows the Create Command SmartGuide with the information for a CICS command bean filled in.

Click To expand
Figure D.3: The Create Command SmartGuide for a CICS command bean.

Clicking the Finish button brings up the Command Editor window shown in D.4. From there, you set the aspects of the command bean, using the property sheet at the bottom of the window. To set the input and output record objects, select the Input and Output entries in the pane in the upper left corner, and right-click. This produces an Add Bean menu item to select from previously created record objects.

Click To expand
Figure D.4: The EAB Command Editor window for CICS.

When you are done with the tool, a Java bean will be created for you that is very easy to use. Via the properties you set in the tool, it is tailored to execute a specific action, such as call a specific CICS transaction. You can now use this Java bean in your own code, by simply setting the input fields (via the set XXX methods for each of the fields in the input record), calling execute to actually call the remote service (e.g., CICS transaction), and then calling get XXX to retrieve each of the output fields from the output record. Finally, export your code, the generated Java bean, and the dependent CCF and connector packages to the file system for runtime. If you intend to run your Java code from OS/390 itself, use ET/390 (discussed later in this appendix) to export these .jar files to OS/390 and optionally compile them with HPJ into an executable and DLLs.

There is much explicit documentation on all of this in the online help for VisualAge for Java. Check there for examples and samples of the connector you are interested in.

A Little More on JRIO

At a high level, Java Record IO (JRIO) is a series of interrelated Java packages, interfaces and classes (called a framework) that offers access to anything record-oriented. The generic framework comes from the VisualAge for Java product, and is called the IBM Java Record Library, while JRIO offers a specific adaptation for OS/390 VSAM files. Anybody, including you, could write similar implementations for other record-oriented data stores, such a Windows file system implementation. In this way, it is much like JDBC, with its interface-driven generic shell that is explicitly defined per database by database vendors. If you write code to any adaptation of the Java record framework, your code will work with minimal changes with any other adaptation, as well as tools build on top of it. The main constructs involved are as follows:

  • Field types describe field attributes.

  • Fields are the actual fields, with data.

  • Record types describe records via one or more field types.

  • Records are the actual records with data.

Further, you can define arrays, field overlays, field alignment, and packing. With the JRIO package, you can write code to do the following with your non-VSAM sequential and partitioned data sets and VSAM entry sequenced data sets (ESDS):

  • Create records.

  • Sequentially scan records.

  • Append records.

  • Overwrite records.

  • Update records.

  • Create, delete, query, rename, and resize files.

  • List files and directories.

JRIO for OS/390 comes by itself, as part of the Java for OS/390 package, and as part of VisualAge for Java Enterprise Edition. For the API details of JRIO for OS/390, refer to www.ibm.com/s390/java.

Creating New CICS Transactions with Java

As you move forward with Java, your decisions regarding CICS transactions come down to three options:

  • Accessing existing or new COBOL CICS transactions using the gateway and connector approach described earlier in this chapter.

  • Writing new CICS transactions entirely in Java.

  • Extending existing COBOL CICS transactions with Java, exposing them to the world as Java CICS transactions.

The IBM CICS team in Hursley has been very busy, extending CICS to give to you all these options. The first option, and how VisualAge for Java can make it relatively easy, has been discussed already.

The latter two options will become increasingly popular, in our opinion. They are enabled by a set of Java classes called JCICS. These classes are available in the CICS Transaction Gateway product, and in the VisualAge for Java product (although you must add the CICS Application Programming Library, using File®Quick Start®Add Feature). These classes are all in the package com.ibm.cics.server. They require CICS Transaction Server 1.3 or higher. Most of the functions of the CICS EXEC API are supported.

You might have expected support for embedded CICS statements inside Java programs, as is the model for other languages. This is not the approach taken for Java and CICS. Rather, the approach taken was to supply to you a set of Java-authored classes for doing typical CICS functionality. This is possible because Java is an OO language. As such, it is easily extended by simply writing new classes. This is much preferable to embedded statements and the pain they cause at compile and debug time.

When you have developed a new Java CICS transaction, you have two choices for deployment. The first choice is to compile and bind it using VisualAge for Java's High Performance Java (HPJ) compiler into OS/390 executable files stored in MVS PDSE libraries and executed by CICS TS 1.3 in a Language Environment run-unit (by using the VisualAge for Java Enterprise ToolKit for OS/390). The other choice is to interpret it with the Java Virtual Machine that CICS supplies, executing under CICS control. We assume most programmers will use the former for performance reasons, although you might find the latter more productive for the initial phases of development. In either case, you can also use the remote debugger in VisualAge for Java ET/390 to debug your Java CICS transaction code remotely from a Windows workstation.

The classes supplied by the package com.ibm.cics.server are listed in Table D.5. Refer to VisualAge for Java's online help for more details. Specifically, look under Concepts®Enterprise access®Program development for CICS TS 1.3®CICS programming services for Java. There, you will see tables mapping the CICS EXEC commands to their equivalent JCICS classes and methods.

Table D.5: CICS Classes in the com.ibm.cics.server Package.
Class | Description
AddressResource | Provides the implementation of synchronization based on the address of a data area. Extends SynchronizationResource. This class is intended for cases of integration with an existing application that uses an address to ENQ. New applications should use the NameResource class. The methods are enqueue/dequeue and get/set Address.
API | An abstract class that provides for common initialization for every class that corresponds to a part of the CICS API. Ensures sufficient initialization of the environment to allow CICS API calls to be made.
AttachInitiator | The main interface to APPC. It extends the API class. You can initiate, and get/set Process, Profile, Queue, SyncLevel, and SysID.
CicsSecurityManager | Provides a security manager for the CICS environment and extends SecurityManager.
CommAreaHolder | Represents a COMMAREA. The actual COMMAREA is a public instance variable of type byte with the name value. On input, value contains the COMMAREA being passed into the program. The program returns a COMMAREA by setting value to a new byte that it has constructed.
Conversation | Implements the interface that is common to both ends of an APPC conversation. There are methods to converse, flush, free, and receive a DataHolder object or send a byte array. You can also issue messages or get ConvId, DataComplete, ErrorCode, ErrorOccurred, NoData, SignalReceived, State, and SyncLevel.
ConversationPrincipalFacility | Extends the Conversation class. There are methods for returning NETNAME, PipData, PrinSysId, Process, and userId.
ConversationSendOptions | Presents a way to specify the following options on the SEND command: INVITE, LAST CONFIRM, WAIT. Methods are clearConfirm/ Invite/ Last/ Wait, getConfirm/ Invite/ Last/ Wait and setConfirm/ Invite/ Last/ Wait.
ConversationState | APPC state constants ALLOCATED, CONFFREE, CONFRECEIVE, CONFSEND, FREE, NOTALLOCATED, PENDFREE, PENDRECEIVE, RECEIVE, ROLLBACK, SEND, SYNCFREE, SYNCRECEIVE, and SYNCSEND.
Cursor | Corresponds to the CURSOR of a CICS terminal, to specify the screen width and height and the initial cursor position in the constructor. The methods include decrement, increment, incrementRow, setColumn, and setRow.
CWAHolder | Holds an array of bytes that is a copy of the contents of the CWA (Common Work Area). The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.
DataHolder | Holds an array of bytes that is the data read from a TDQ (Transient Data Queue). The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.
EnterRequest | Provides the Java equivalent to the EXEC CICS ENTER command. It extends the API class with properties (with get/set methods) that include TraceIdentifier, Resource (a name), and Exception (Boolean).
ESDS | An implementation class for ESDS files that extends KeyedFile. The methods are delete, read, readForUpdate, rewrite, startBrowse, and write records. It supports alternate keys. The read methods take RecordHolder objects.
ESDS_Browse | Implements the CICS File Control interface for browsing ESDS files via RBA. It extends KeyedFileBrowse with the methods next, previous, and reset. The next and previous methods take a RecordHolder object.
FileBrowse | A superclass of all of the browse-related implementation classes.
ItemHolder | Holds an array of bytes that is the data read from a TSQ (Temporary Storage Queue). The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.
KeyedFile | An implementation class for the KeyedFile interface that extends the abstract superclass File. There are methods to read, readForUpdate, readGeneric, readGenericForUpdate, rewrite, startBrowse, startBrowseGeneric, and write records.
KeyedFileBrowse | An implementation class for browsing a keyed file. that extends FileBrowse. The methods are next, previous, and reset. The next and previous methods take a RecordHolder object and KeyHolder object.
KeyHolder | Holds an array of bytes that is the key of a file. The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.
KSDS | An implementation class for KSDS files that extends KeyedFile. It adds or overrides methods to delete, deleteGeneric, and write records, by key. The key is given as a byte array.
NameResource | An implementation for named synchronized resources that extends SynchronizationResource. It has methods for set/get name, and enqueue/dequeue.
Program | The Java interface to CICS Program Control, equivalent to the LINK and XCTL commands. It extends RemotableResource and includes methods for linking (link) and transferring control (xctl).
RecordHolder | Holds an array of bytes that is the data read from a file. The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.
Region | Corresponds to a CICS region. It includes methods to set/get APPLID, CWA, and SYSID.
RemotableResource | An abstract class providing common implementation for resources that can be accessed on other systems. It extends Resource and adds the sysId property for naming the remote resource's system.
Resource | An abstract class that provides the common implementation for named resources (program, TSQ, etc). It extends API and adds the properties name and description (gettable/settable).
RetrievedData | Holds data retrieved for a task that has been started.
RetrievedDataHolder | Holds values that can be retrieved for a (started) task. It is similar to CommAreaHolder.
RRDS | An implementation class for the RRDS files that extends the abstract, non-public superclass File. There are methods to delete, read, readForUpdate, rewrite, startBrowse, and write records.
RRDS_Browse | Implements the CICS File Control interface for browsing RRDS files via RRN. It extends FileBrowse with methods for next, previous, and reset. The next and previous methods take a RecordHolder object.
SearchType | Provides constants defining search types that can be used with generic keys: EQUAL and GTEQ.
StartRequest | Provides the Java equivalent to the EXEC CICS START command. It extends RemotableResource. Its methods include issue and cancel for starting and canceling the start request, as well as set/get methods for the properties time, ID, terminal, rTransaction, and rTerminal (r is for retrieved), queueName, checked, and isProtected.
SynchronizationResource | An abstract class that provides the common implementation for synchronized resources. It has methods for set/get description and enqueue/dequeue. It extends API and is extended by NameResource and AddressResource.
SyncLevel | Provides constants for the different synchronization levels that can be specified for an APPC conversation: CONFIRM, NONE, and SYNCPOINT.
Task | Corresponds to a CICS task. It extends API, adding methods to commit and rollback, abend, retrieve data, and get FCI, PrincipalFacility, QNAME, STARTCODE, TaskNumber, TransactionName, TWA, and USERID. You can also setTWA. You don't create your own Task objects; you call the getTask static method to return to you the Task object for the task your thread is running in. Its important public fields are err and out, which are like System.err and System.out. You use the println methods in them to write text directed to the user's terminal (if the Principal Facility is a terminal) or to System.out.
TCTUAHolder | Holds an array of bytes that is a copy of the contents of the TCTUA. The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.
TDQ | The Java implementation of CICS Transient Data Queues (TDQs). It extends RemotableResource. Added methods include delete, readData, readDataConditional, and writeData.
Terminal | Provides the Java interface to a CICS Terminal and extends API. You can get the Cursor, Name and User, and get/set the NextCOMMAREA, or NextTransaction.
TerminalPrincipalFacility | Extends Terminal. You can clear, converse, erase, receive, receiveBuffer, send, sendControl, sendText, and waitTerminal. The converse, receive, and receiveBuffer methods all take DataHolder objects as parameters. You can also get NextCOMMAREA, NextTransaction, TCTUA, and TERMCODE. You can set NextCOMMAREA and TCTUA.
TSQ | Provides the Java interface to CICS Temporary Storage Queues and extends RemotableResource. You can delete, readItem, readNextItem, rewriteItem, rewriteItemConditional, writeItem, and writeItemConditional. The readItem and readNextItem methods take an ItemHolder object as a parameter. You can also get/set the Name, QueueName, and Type of the TSQ.
TSQType | Provides constants for the different types of TSQs: AUXILIARY and MAIN.
TWAHolder | Holds an array of bytes that is a copy of the contents of the TWA. The actual data is an instance variable of type byte with the name value. It is similar to CommAreaHolder.

When you write a Java CICS application, your first or entry class will have a main method as usual, but instead of expecting an array of String objects, it must expect a CommAreaHolder object, as that is what CICS will pass it. The entry class is identified on the HPJ -main option when you statically compile your application.

Typically, your Java CICS code will instantiate objects like an ESDS, KSDS, RRDS, Program, StartRequest, TDQ, or TSQ. After creating the object, you will call setName to name it. You will also use the getTask static method in the Task class to get the current Task object, and perhaps call getTransactionName on it to get the name of the current transaction. The important method in the Task object, though, is out.println, for outputting text to the console.

When working with CommAreaHolder data, you have to work with the byte array field called value. Since it is a byte stream, you have to use the stream classes in the java.io package to work with it:

  • ByteArrayInputStream. Give it a byte array, and it makes a stream object readable byte by byte.

  • BufferedInputStream. Give it a ByteArrayInputStream, and it makes a buffered stream with the ability to read sub-byte-arrays.

  • DataInputStream. Give it a BufferedInputStream, and it gives you an object with read XXX methods for successively reading the fields out of the buffer. There is one read XXX method for each of the Java data types, such as readInt that returns an int primitive value. Use readInt if the next item in the buffer is an integer value.

Here is an example:

ByteArrayInputStream bais =
   new ByteArrayInputStream(aCommArea.value);
BufferedInputStream bis = new BufferedInputStream(bais);
DataInputStream dis = new DataInputStream(bis);
int new_field1 = dis.readInt();

Similarly, to write to the byte array value held in the CommAreaHolder, you use the classes ByteArrayOutputStream, BufferedOutputStream, and DataOutputStream. The last has methods of the form write XXX for appending Java data type variables to the byte array stream. That is, writeInt writes a primitive int value to the end of the byte stream, for example:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(baos);
DataOutputStream dos = new DataOutputStream(bos);
int field1 = 654321;
dos.writeInt(field1);
dos.flush(); // ensure bytes are written to stream
aCommArea.value = baos.toByteArray();

If you add the VisualAge for Java feature CICS Application Programming Samples, you will find in the project of the same name a number of examples. Listing D.2 is a simplified version of one of the examples, which has a program that has been linked by another program, and is passed a simple integer value. Note that via the link and xctl methods of the Program class, you can access other Java CICS programs or CICS programs written in any other language, such as COBOL.

Listing D.2: A linked program with a passed integer value.
Start example
   import java.io.*;
   import com.ibm.cics.server.*;

public class ClassTwo
{
   public static void main(CommAreaHolder CAH)
   {
      Task t            = Task.getTask();
      byte MyCommArea[] = CAH.value;
      if (t == null)
         System.err.println(
            "ProgramControlExample: Can't get Task");
      else
         try
      {
         t.out.println("Entering ProgramControlClassTwo.main()");
         if (MyCommArea.length > 0)
         {
            ByteArrayInputStream bais =
               new ByteArrayInputStream(MyCommArea);
            BufferedInputStream bis = new BufferedInputStream(bais);
            DataInputStream dis = new DataInputStream(bis);
            int new_field1 = dis.readInt();
            t.out.println("data received correctly");
         }
         else
            t.err.println("Transaction invoked without a CommArea");
      } catch (Throwable th)
      {
         System.err.println("Failed to read COMMAREA");
      }
   } // end main
} // end ClassTwo
End example

CICS and CORBA

Common Object Request Broker Architecture, or CORBA, is an industry standard defined by the consortium of companies that participate in the Object Management Group (OMG). Outside of the competing Microsoft offering, Distributed Common Object Model (DCOM), CORBA has become a de facto standard for doing distributed object communications. The idea is very similar to RMI (discussed in chapter 14), in that an object lives on the server, but clients communicate with it by calling methods in a local client proxy object. Those method calls are seamlessly marshalled across the network to the real object on the server, where they are executed and the results returned.

The underlying protocol for CORBA is Internet Inter-ORB Protocol (IIOP), which is built on top of TCP/IP. The differences between RMI and CORBA are as follows:

  • RMI requires only a JVM on the client and server, and the JDK-supplied RMI registry on the server. CORBA requires an Object Request Broker (ORB) for the server and a smaller version for the client. Many vendors supply ORBs now, usually in two flavors: C++ with bindings for numerous compiled languages, and Java.

  • By default, RMI uses its own protocol on top of TCP/IP, although it now also supports IIOP as an alternative.

  • RMI is for Java-to-Java communications only. CORBA can be used to communicate between any two languages on the client and server, as long as the ORB provider supplies bindings for those languages.

  • CORBA goes beyond just raw communications and offers your distributed objects additional optional functionality, such as transactions, naming, security, persistence, and lifecycle. RMI does not offer this functionality directly, but rather defers to Enterprise Java Beans for these services (or CORBA).

To a large degree, CORBA and EJBs offer similar functionality. However, think of Enterprise Java servers as offering the services of CORBA for Java only, but relying on CORBA itself (or RMI) for the distribution functionality.

Those services are important for large, secure, scalable, mission-critical applications. That is why IBM offers them and more in its WebSphere Application Server, Enterprise Edition. For CICS customers, some of these enterprise-level services have always been available via CICS itself. What CICS did not offer, historically, was any method for distributing those CICS transactions via CORBA IIOP. Now that you can write CICS transactions in an object-oriented programming model with Java and JCICS, the CICS team has decided it is time to offer this. As of version 1.3 of CICS Transaction Server, there is now a built-in CORBA ORB that allows you to access your Java CICS (JCICS) transactions via IIOP.

To allow your JCICS transactions to be accessed by any CORBA client talking IIOP, you must statically compile and bind your JCICS code using the High Performance Java (HPJ) compiler that comes with VisualAge for Java Enterprise Edition for OS/390 (discussed next). You cannot use the CICS-supplied JVM to interpret IIOP-capable transactions.

Only a subset of the CORBA services are supported, since transaction and security contexts, for example, are already supplied by CICS itself. CICS also leverages eNetwork Communications Server in OS/390 Version 2 Release 5 or higher for TCP/IP port sharing, and MVS Domain Name Server (DNS) and Workload Management (WLM) services for balancing IP connections and workload in a Sysplex domain. Built-in CICS services balance method-call invocations across CICS regions, offering further workload balancing. The CORBA services that are not supported by CICS are Name Server, Security, Lifecycle, Externalization, Persistence, Concurrency, Interface Repository Framework, and Location.

To distribute a JCICS transaction via CORBA, you must code the method interfaces using OMG's Interface Definition Language (IDL), which is a language-neutral equivalent to Java's interfaces. (IDL is defined in Common Object Broker: Architecture and Specification at the Web site www.omg.org/library.) You then use the CICS-supplied IDL compiler to convert the IDL to Java code. That code is really just a collection of classes and method stubs or signatures.

Your job, then, is to "flesh out" those methods with your actual business logic, probably using the JCICS classes to access CICS services, or to access existing COBOL CICS programs, which is a great way to effectively distribute existing transactions to the world in a scalable and secure way. This is for the server-side Java code. For the client side, you need a stub or proxy object, and using the client side ORB's IDL compiler provides that. It is your choice which vendor's ORB to use on the client.

VisualAge for Java Enterprise Edition includes a useful IDL development environment inside it to assist in these steps. It also fully documents them with respect to CICS. See the online help at Concepts®nterprise access®Program development for CICS TS 1.3®The Interface Definition Language (IDL), and also Concepts®IDL Development Environment.

The tricky part, though, is that your distributed JCICS classes must be stateless, without relying on instance variables. Rather, each class is merely a collection of methods, each of which represents a single transaction to the client code. If information must be stored between method calls, either that information must be passed via parameters, or a key value must be passed and used to retrieve information saved in a data store.

We leave the fine-grained details of this to you to explore via VisualAge for Java's online help. IIOP is a great option as an alternative to using Java Servlets to access your non-IIOP JCICS transactions. With Servlets, your client code will not be just any TCP/IP client, but rather an HTML page inside a Web browser. Depending on your client requirements, both are valid programming models. You might choose to actually do both, accessing compiled JCICS IIOP transactions from your interpreted Java Servlets via IIOP.


VisualAge For Java Enterprise Edition For OS/390

This appendix concludes by describing (briefly) the IBM VisualAge for Java product. Specifically, this section discusses what it has to offer OS/390 Java programmers.

You know from chapter 14 that VisualAge for Java comes in three varieties: Entry, Professional and Enterprise. Not surprisingly, OS/390 programmers benefit most from the Enterprise edition. It contains everything in the Professional edition, plus "grown-up" stuff like Team Support. It also comes with the Common Connector Framework and the Enterprise Access Builders mentioned earlier in this appendix, as well the Enterprise Toolkit for OS/390 (ET/390). This feature helps you write Java code within the IDE, export it to OS/390, and compile, run, debug, and trace it there. What is truly exciting is that it not only supports interpreted Java on OS/390, but it also supports truly compiled Java on OS/390.

ET/390 includes something known as the High Performance Compiler for Java, or HPJ. HPJ is a Java compiler that runs on OS/390 and produces executable load modules or DLLs from your packages. These will run either under UNIX System Services or CICS itself. In the near future (maybe as you read this), it will also be possible to run natively compiled Java DLLs as IMS transactions and as DB2 Stored Procedures.

To OS/390, HPJ modules look like C- or COBOL-authored modules. Of course, the point of compiling your Java is that it is faster than interpreted Java, sometimes significantly so. However, over time, that performance gain will be reduced as the interpreter continues to make significant gains in performance.

The combination of the VisualAge for Java Enterprise Edition, and the stuff it needs on OS/390 itself, such as the compiler and runtimes, is packaged together into one OS/390 product called VisualAge for Java Enterprise Edition for OS/390. The Enterprise Toolkit for OS/390 (ET/390) in VisualAge for Java offers tooling specifically to address the needs of programmers writing Java code that will run on OS/390. It complements all the other functionality in the VisualAge, such as the CCF and EABs. Table D.6 shows the functionality available in ET/390.

Table D.6: VisualAge for Java ET/390 Support for OS/390 Programmers.
Feature | Description
Export to OS/390 | Exports selected packages or classes in the IDE to the S/390 HFS. The target location is specified once in the properties, and reused and shared by all in the team. As of VAJava 3.0, this requires an NFS mount to your OS/390 file system.
Remote Run | Runs the class selected in the IDE remotely on OS/390. You can run interpreted Java via the JVM or compiled Java executable modules. Options are specified once, and reused and shared by all in the team. Anything written to standard out by Sys-tem.out.println is captured and displayed in the local console window.
Remote Compile | For creating executable load modules or DLLs for either HFS or CICS/ESA. Compiles the class selected in the IDE remotely on OS/390 using previously specified options, which are specified once, and then reused and shared. You can maintain two sets of options, one for production and one for test.
Remote Debug | Debugs the interpreted or compiled class selected in the IDE remotely on OS/390. The source is displayed locally on Windows, while the application runs on OS/390. Again, options are specified once, and then reused and shared.
Remote Performance Analyzer | Traces object execution of your OS/390 Java code.
Jport Tool | Checks your Java code for portability concerns.

This information is valid as of Version 3.0 of VisualAge for Java, but each release brings more functionality. For more information on this functionality, refer to the Getting Started manual of the Enterprise Edition of VisualAge for Java.


Appendix E: Using Java On OS/400

Overview

This appendix offers a brief tour of getting, installing, and using Java on OS/400. Al- though you might start your Java journey by learning Java while on your personal computer, OS/400 is committed to being a first-class Java platform based on its own merits. That means you can run Java applications on OS/400 itself.

Because Java is so portable, there are not too many special instructions for doing this, but there is some additional information that will give you a leg up. Using Java on OS/400 involves at least a passing knowledge of the following:

  • AS/400 Developer Kit for Java. The JDK.

  • QShell Interpreter. The UNIX shell required for working with the JDK commands.

  • AS/400 Toolbox for Java. A set of pure-Java classes for accessing OS/400 resources from OS/400 Java or client Java.

  • VisualAge for Java. IBM's premier development product for Java.

Each of these is covered in a bit more detail in this appendix. If you decide to use Java Servlets and JavaServer Pages (JSPs), you need to read about the WebSphere Application Server and the IBM HTTP Server. As of V4R3, everything you need to run Servlets and JSPs is built into OS/400. Enterprise Java Beans (EJBs) are another topic you might want to explore. EJBs are available with the Advanced edition of WebSphere Application Server. This is not part of the base operating system, but an optional product. For more on Servlets, JSPs, and EJBs beyond what is covered here and in chapter 14, refer to www.ibm.com/as400/websphere.


Installing The Components

Table E.1 provides information for installing the various pieces, at a Version 4 level. Note that versions of the operating system after V4 will have a number other than 5769.

Table E.1: OS/400 Java and Related Products Installation Information.
Product | Product Number | Notes
AS/400 Developer Kit for Java | 5769JV1 | You can choose from multiple JDKs to install and use. Each is a different option.
QShell Interpreter | 5769SS1, Option 30 |
AS/400 ToolBox for Java | 5769JC1 |

To find out if these products are installed, use GO LICPGM, and press F11 after finding the products in the list. They will show *COMPATIBLE in the Installed Status field. If they are not in this status, use option 1 to install them. If they are on the list, use RSTLICPGM to install them from the CD-ROM.


The AS/400 Developer Kit For Java

As mentioned in chapter 1, each release of JDK is written in its original form for Sun Solaris and Microsoft Windows operating systems by Sun Microsystems. IBM licenses this, and the IBM Hursley laboratory in England ports it to all operating systems.

The OS/400 team in Rochester, Minnesota then pushes it deep into the operating system to get great performance. Because the JDK is standard, and by the terms of the licensing agreement IBM and others can only add to it, not take away from it, all the standard JDK commands are available on OS/400, including java, javac, javap, javadoc, and jar, among others. Of course, these are not the standard OS/400-style command names that you are used to. They have to work on Java source files and class files that are ASCII-based, not EBCDIC-based the way COBOL source and programs are. This is because the goal is to be able to run any Java class file from anywhere, without a change or even a recompile. To enable all of this-that is, to make OS/400 Java support as industry-standard as possible-requires that the JDK be based not in the library/file native file system, but on the Integrated File System.

Using the Integrated File System and QShell Interpreter

COBOL development uses the native OS/400 file system, which involves libraries inside QSYS, files and other objects inside libraries, and members inside files. Files, and hence their members, are either data files (Physical Files or Logical Files) or source files (Physical Source Files). There are other file types, too, such as display files and printer files. The underlying collating sequence is EBCDIC, which is different from the ASCII sequence used by other operating systems (except for OS/390, which is also EBCDIC-based).

The Integrated File System (IFS) is an alternative to the native file system. It is actually multiple file systems in one, but the one you are most interested in is the root file system. This is very similar to the file system on Windows. It has directories that can contain other directories and stream files, much like Windows. Stream files are simply flat files that contain a sequence of bytes, with no record partitioning and no relational database constructs, again, just like files on Windows.

The highest-level directory is called the root. It is designated by a single forward slash, /. Unlike Windows, the IFS uses a forward slash for path names, not a backward slash. So, if you have a file called HelloWorld.class in directory myJava, which in turn is in directory Phil, the fully qualified path name of the file is

/Phil/myJava/HelloWorld.class

Note that directory and file names are case-sensitive in the IFS. If a given path name does not start with a forward slash, then it is relative to the current directory.

Prior to Java in V4R2, you could only work with the IFS through OS/400 CL commands. For example, to create a directory, you use the CRTDIR command, as in CRTDIR DIR('/PHIL') and CRTDIR DIR('/PHIL/MYJAVA'). You have to create the parent directory before creating the child directory. Each job has a "current directory" that can be determined by the command DSPCURDIR, and changed by the command CHGDIR. To list files and work with them in a given directory, you use the command WRKLNK. To see all the IFS CL commands, type GO FILESYS.

These CL commands are still of value. For example, the first thing you need to do is create a directory for your Java files. First, check to see if the administrator has created one for you in \home\UserID, where UserID is your user ID. Use WRKLNK to look for this directory. If one doesn't exist, use CRTDIR to create it. Then, to always make it your default current directory, use the CHGPRF (Change Profile) command and the HOMEDIR parameter, like this:

CHGPRF HOMEDIR('/Phil/myJava')

Of course, you might need to get the system operator to do this for you, if your profile lacks the necessary authority.

Once you have a directory, and you have made it your current directory, you can use the new (as of V4R2) QShell Interpreter. To start it, use the CL command STRQSH or QSHELL. This puts you in a command shell where you can enter standard UNIX commands instead of OS/400 CL commands. This will be more comfortable if you are familiar with UNIX. It is also where you enter JDK commands, but we'll get to that later in this appendix.

Table E.2 lists some of the common commands you might use on Windows, and their equivalent IFS commands-both the CL version and the QShell version. This table should be enough to get you going with IFS. More detailed information is available at www.ibm.com/as400/infocenter.

Table E.2: Windows, IFS CL, and QShell Commands.
Command | Windows | CL | QShell
Show the current directory name. | cd (no parameters) | DSPCURDIR | pwd
Change the current directory. | cd path or \path | CHGCURDIR | cd path or /path
Make a directory. | md or mkdir | CRTDIR | mkdir
Delete a directory. | rd or rmdir | RMVDIR | rmdir
Rename a directory. | rename |  |
Display or change the directory owner. | explorer |  | chown
Display or change the directory authority. | attrib | DSPAUT, CHGAUT | chgrp, chmod
List the files in a directory. | dir | WRKLNK | ls
Display a file's contents. | type |  | pr, cat, tail, head
Copy a file. | copy | COPY | cp
Move a file. | move | MOVE | mv
Delete a file. | erase | WRKLNK | rm
Rename a file. | rename | RENAME | mv
Display or change the file owner. | explorer | WRKOBJOWN | chown
Display or change the file authority. | attrib | DSPAUT, CHGAUT | chgrp, chmod
Create a link. |  | ADDLNK | ln
Define an alias (shortcut) command. |  |  | alias
Exit the shell or prompt. | exit |  | exit
Run a CL command. |  |  | system
Search files for a string. |  |  | grep
Set an environment variable. | set | WRKENVVAR | export
Display previously entered commands. |  |  | history
Compress and uncompress files. | winzip or jar |  | tar/untar or jar
Get online help for a command. | help |  | man

Working with Java on OS/400

Now that some basics are out of the way, it is time to focus on working with Java. To run a Java class on OS/400, you enter the QShell Interpreter, ensure you are in the directory containing the Java class file, and then run it as usual using the java command. It is that simple. All of the JDK commands are there, and they are identical to their Windows counterparts-well, actually, their UNIX counterparts, but other than remembering to use a forward slash (/) instead of a backslash (\) for path names, it is the same.

Your first order of business is to get a Java class to run. You have two choices to do that: either create the .class file using javac on Windows (say) and copy that to OS/400 IFS, or copy the .java source file from Windows (say) and use javac on OS/400 to create it. In either case, Java development is usually done on a workstation running Windows (say) and Java development tools like VisualAge for Java.

You don't use old workhorses PDM and SEU to create Java applications, because these tools work on EBCDIC-based native file system objects, and Java is a new breed of ASCII-based IFS objects. If you are really addicted to minimalist green-screen tools, you could use SEU to edit a source member containing Java, and then use CPYTOSTMF (Copy To Stream File) to copy that member to an IFS file. We frown on this, though. Java is an exciting new language. It makes sense to use exciting new tools for it.

You have several options to get your .java or .class files from Windows to OS/400 IFS. A few of the most common are listed in Table E.3. Pick whatever you like, or whatever is handy.

Table E.3: Windows, IFS CL, and QShell Commands.
Option | Description
Use ftp (File Transfer Protocol). | If your TCP/IP configuration allows this, entering ftp from the Windows command line is an easy way to copy any file to OS/400.
Use client access | If you have client access, map a network drive to your system and simply use the Windows copy command to copy any file to OS/400 via the mapped drive.
Use CODE/400 | If you use CODE/400 anyway for your COBOL, CL and DDS programming (and you should), it has support for remote access to the IFS.
Use VisualAge for Java | The Enterprise Toolkit for AS/400 in the Enterprise Edition has easy-to-use support for exporting your Java source or class files to OS/400 IFS, directly from the IDE. You just specify the target location once, and it remembers it every time. This is the easiest option.

The only one of the options in Table D.3 that you might need help with is FTP. Here is a sample script from a Windows DOS prompt window:

c:\Phil\myJava>ftp MYSYSTEM
User: coulthar
Password:
230 COULTHAR logged on.
ftp> cd /Phil/myJava
250 "/Phil/myJava" is current directory.
ftp> binary
200 Representation type is binary IMAGE.
ftp> put HelloWorld.class
467 bytes sent in 0.02 seconds
ftp> ascii
200 Representation type is ASCII
ftp> put HelloWorld.java
206 bytes sent in 0.03 seconds
ftp> quit

Setting CLASSPATH in QShell

Now your Java files are in the IFS, and you know how to find your way to them and run them using QShell. You are on your way. The last thing you really need to know is how to set your CLASSPATH so that Java can find your Java classes. By default, it only looks in the current directory, but that is not very useful. You will often have classes spread over multiple directories.

The first important note about the CLASSPATH environment variable on OS/400 is that it is based on UNIX, so it uses a colon to separate the entries, not a semicolon as in Windows:

/Phil/myJava:/George/myJava

To set the CLASSPATH dynamically, that is, each time you run Java, use the -classpath parameter to the java command, like this.

java -classpath /Phil/myJava:/George/myJava HelloWorld

This is nice, but it is a pain to remember each time. To make it more permanent, you can set it once for your QShell session using the export command, much like the SET command in Windows. Here is an example:

export -s CLASSPATH=/Phil/myJava:/George/myJava

If you type this in your QShell session, that CLASSPATH remains in effect for the duration of the session. To see all current environment variable settings, type export with no parameters. By the way, the -s option tells QShell to make this not only a variable for use by QShell scripts, but also an environment variable for use by applications like the Java interpreter.

This is a little better, but only marginally. To get more permanence, create a file called .profile ("dot profile") in your home directory in the IFS and put an export command in it, as shown here:

export -s CLASSPATH=.:/Phil/myJava:/George/myJava

Use the same options discussed for .java files to create your .profile file and copy it to the appropriate IFS directory. This is an example where SEU and CPYTOSTMF might be just fine.

Note that the example included a period, which represents the current directory. We recommend always putting this on your CLASSPATH, even in Windows. For this .profile to work correctly, it must be in your home directory, as specified in the HOMEDIR of your user profile. By convention, your home directory should be /home/userID, where userID is your user profile name.

You will probably most often use the .profile option for your own personal Java exploration. However, eventually you or your system programmer might install a Java application for general use on your system, and the time will come to set the CLASSPATH globally for all users. To do this, place the .profile file in a special directory: /etc/profile. In there, it will be applied to all users on the system.

The .profile file probably already exists in this directory, so you will want to append to it instead of creating it. To do this, use CPYFRMSTMF to copy into a source member for editing by SEU. Alternatively, just use CODE/400, which has support for directly editing all your IFS source files. (See www.ibm.com/software/ad/varpg for a downloadable, free trial copy.)When running a Java program, the CLASSPATH specified in /etc/profile/.profile is checked first when looking for Java classes, and the CLASSPATH specified in your home directory .profile is searched next if the class is not found. Thus, these two files are cumulative, not mutually exclusive.

Using CL Commands for Java, and the Java Transformer

If you could only run Java classes from QShell, you might be in trouble if you wanted to mix COBOL and Java in the same application. How could COBOL call Java if there is no CL command to invoke via the command analyzer API, for example? Well, there is an alternative to the java command in QShell: the RUNJVA CL command, or its identical twin, the JAVA CL command. These commands allow you to run a Java class directly from the OS/400 command line, a CL program, or any of the traditional ways of running CL commands. The Java class files still exist in the IFS; these CL commands will run the QShell java command behind the scenes.

To use them, you specify the class file on the CLSF parameter, using the usual, fully qualified syntax for IFS files, like this:

RUNJVA CLSF('/Phil/myJava/HelloWorld')

The only tricky part is specifying the CLASSPATH in this situation. Use the CLASSPATH parameter to specify it dynamically, similar to the -classpath option on the java QShell command, or specify it in the CLASSPATH environment variable using the ADDENVVAR or CHGENVVAR commands. The syntax for the path itself is the same discussed for QShell.

Note that the CL CLASSPATH environment variable only pertains to you, and only lives as long as your job (much like the QTEMP library). This means you have to add the ADDENVVAR command to a CL program that is run when you sign on, if you want permanence. Also, note that this CLASSPATH is only searched after the CLASSPATH in the export -s classpath statement in /etc/profile/.profile, as was the case for .profile in your home directory. By the way, that profile will be run, too, since RUNJVA will invoke QShell, and run .profile if it is in your home directory. Really, then, putting the .profile file in your home directory is the best option all around for setting up CLASSPATHs persistently and uniquely for you.

When you use RUNJVA, it actually does a little more than the java command in QShell. It truly compiles your java class into OS/400 machine code (PowerPC instructions) the first time you run a class. This is the work of the Java Transformer. On subsequent runs, that machine code is executed, instead of interpreting the bytecode. This truly compiled Java is much faster than interpreted Java.

Behind the scenes, a new object is created-a *JVAPGM (Java Program) object. The .class file remains untouched, so it can still be copied to another operating system and run there, which means portability is not affected. So, you get the best of both worlds-the performance benefits of compiled Java and the portability of interpreted Java. If you subsequently change the .class file by replacing it with a new version, the Java Program object is invalidated, so it is re-created when you next use RUNJVA.

You can explicitly compile a single class file or an entire .jar file of classes in a separate step if you prefer, using the CRTJVAPGM command. In both commands, you can specify the optimization level, anywhere from 10 to 40. The lower number is best for debugging, while the higher number offers the most optimization.

Two other commands to use for your Java Program object are DLTJVAPGM and CHGJVAPGM (for example, to change the optimization). Note that DLTJVAPGM only deletes the hidden *JVAPGM object, not the original class file. Finally, there is a DSPJVAPGM command, which displays attributes about the Java program, such as its optimization level.

By the way, you can use the system debugger (STRDBG) to debug a Java application. You might want to use interpret mode versus compile mode for this, in which case you can specify INTERPRET(*YES) on the RUNJVA command.

Java Remote AWT

Java on OS/400 always runs in a batch job, even when you run it from an interactive session, but you can still run GUI applications there. There is special support added for OS/400 (and OS/390) that allows you to remotely distribute the GUI API calls (that is, the AWT method calls) to a GUI-capable client. This technology is called Remote AWT (RAWT).

RAWT requires that you be running from a GUI-capable client (Windows or Network Station), using, say, an emulator window to start the Java application, and that you have pre-registered the IP address of your workstation. You also have to download the runtime classes needed to your client, and update your CLASSPATH to point to them. All this is described in the documentation, so it is not discussed here.

We feel it is better for you to explicitly split your applications between client and server, and use a middleware technology such as the IBM AS/400 Toolbox for Java, IBM Message Queue Series (MQ), OMG CORBA IIOP, or Java Remote Method Invocation (RMI) to communicate between the two. The Remote AWT technology is nice for experimentation, but it is really too slow for most production applications due to the heavy network traffic it generates. As of this writing, there is no Remote Swing technology, but it is expected soon. Word has it that it is a significant improvement over Remote AWT, so you might want to check it out at www.ibm.com/alphaworks.

For detailed information on all of this, including explicit instructions for debugging and other information, see the AS/400 Developer Kit For Java manual. It is available by following the Java link at www.ibm.com/as400/infocenter.html.


AS/400 Toolbox For Java

The Java Program that the Java Transformer creates is not an ILE service program because Java is not an ILE language. You cannot bind or link a Java program into a non-Java ILE program or a service program. Thus, to "get out" of Java and access your data, other programs, data queues, message files, and so on, you have to rely on some unique-to-OS/400 Java classes written by the IBM Rochester team. These classes are called the AS/400 Toolbox for Java. You are going to love these! They are well-designed, well-written, and come for free with the system (as well as with CODE/400, VisualAge for Java, and with Inprise J/Builder).

Because these classes are written entirely in Java using only industry-standard TCP/IP sockets for their communication layer, they run everywhere. By extension, then, so does your code that uses them. This means you can write Java code to access your DB2/400 files or your COBOL programs, and run that Java code anywhere. Run it on the same AS/400, a different AS/400, Windows, Linux, UNIX, a Web Browser, or a network station. Even in a personal device. Anywhere. This is the ultimate OS/400 middleware!

The downside (there always is one, isn't there?) is that while your code will run anywhere, it will always be trying to talk to an AS/400 somewhere in the world. So, if you are interested or worried about server portability, you will have to forgo the Toolbox or carefully isolate its usage so your move to OS/390 is easy, for example. The one exception to this rule is the JDBC driver inside the Toolbox, described in chapter 13. JDBC is the one portable set of classes in the Toolbox, since it is an industry standard and not unique to OS/400. To switch databases, you need only change the code to register the driver and connect to the database (assuming your SQL statements are standard).

At the time of this writing, there were more than 500 classes in this product, and there are more to come. A brief description follows of the functionality that these classes provide. Refer you to the IBM redbook Accessing the AS/400 System with JAVA (SG24-2152) for a well-done and more detailed introduction to these classes, as well as the manual AS/400 Toolbox for Java, available at www.ibm.com/as400/infocenter.html. Table E.4 lists the functions offered by the AS/400 Toolbox for Java, in what is perhaps their order of importance to most Java applications.

Table E.4: AS/400 Toolbox for Java Functions as of V4R4 (V3R2M2).
Function | Description
Data access via JDBC | SQL access, as described in chapter 13.
Data access via record-level access | Much like COBOL's READ, WRITE, REWRITE, and DELETE statements.
Command Call | Runs an OS/400 command in batch. Any messages sent by the command are returned to your Java application.
Program Call | Runs any program object in batch. Parameters can be passed and updated. Any messages sent by the program are returned to Java.
Data Queues | Enables your Java application to create, read, write, and delete data queues, both sequential and queued. It can also work with the attributes of a data queue.
Data Areas | Enables your Java application to create, read, write, and delete data areas. It can also work with the attributes of a data area.
User Space | Enables your Java application to create, read, write, and delete user spaces. It can also read and write the attributes of a user space.
IFS File System | Enables your Java application to access directories and files in the IFS. This is a superset (containing super classes) of java.io in the JDK, tweaked specifically for local or remote IFS access.
Network Print | Enables your Java application to work with spooled files, output queues, printers, printer files, writer jobs, and AFP resources. Yes, you can create reports, but it is pretty scary stuff because you have to create SCS data streams. Reading a spool file is pretty cool, though.
System Values | Enables your Java application to read and change system values and network attributes.
Jobs | Enables your Java application to retrieve lists of jobs (all, or by name, number, or user) and job log messages, and read details about a particular job.
Users and Groups | Enables your Java application to retrieve lists of users and groups.
System Status | Enables your Java application to retrieve system status information. It can also access system pool information.
Permissions | Enables your Java application to retrieve and change OS/400 object authorities.
Digital Certificates | Manages digital certificates, which are used for secure transactions over the Internet. For example, they are used by Secure Socket Layer (SSL).

These major functions are offered through a myriad of classes. In order to use these classes, numerous other helper classes are also supplied that are quite useful in their own right. (The JDBC classes do not require or use these explicitly. They are used behind the scenes.) A list of these helper classes is given in Table E.5. All of these classes are in a package named com.ibm.as400.access.

Table E.5: AS/400 Toolbox for Java Helper Classes.
Helper Classes | Description
AS400 object | All service classes except JDBC require one of these as input into their constructor. This class manages OS/400 log-ins, optionally prompting the user for an ID and a password (on GUI clients).
AS/400 data types | These classes help you easily translate OS/400 values to and from appropriate Java data types.
AS/400 messages | This class represents an OS/400 message returned from a program or command call.
AS/400 record format information | These classes represent DB2/400 field definitions, record formats, and actual data records. They are used when doing record-level data access, and can be used when working with program calls and data queues.
AS/400 QSYS object path name | This represents objects in the IFS system. Because the QSYS file system can be accessed through the IFS, you can use IFS objects to get at library-based objects-for example, "/qsys.lib/mylibr.lib/myfile.fil." This helper class (QSYSObjectPathName) makes it easy to convert native file-system object names to IFS-style path names, which all the Toolbox classes expect. Specifically, the static method toPath takes library, object, and object type names and returns a string of the form above.
Exceptions | The Toolbox has a rich set of its own exception objects.
Trace | Using the Trace class, you can enable logging to help with problem determination. There are five levels of tracing you can enable: information, warning, error, diagnostic, and data stream.

The AS/400 Toolbox for Java also contains a number of "visual classes," which are Java Swing GUI components that use these base non-visual classes. The visual classes are found in package com.ibm.as400.vaccess. They are designed to be easily embedded inside your own Swing GUI applications, saving you some time you would otherwise spend writing them yourself. They all leverage the concept of reusable panels, as discussed in chapter 12.

Note that these classes, up to V4R5 of the AS/400 Toolbox for Java, use the JDK 1.1.x version of Swing, which has to be separately downloaded from www.javasoft.com. The V4R5 version of the AS/400 Toolbox for Java uses the JDK 1.2.x built-in Swing version. The Toolbox's product numbering is different than OS/400, so the V4R4 version is V3R2M2, for example, while the V4R5 version is V3R2M3. The GUI classes have to run on a client, so regardless of your OS/400 level, you can always use the latest level of the Toolbox when running Java code on a client. The only thing to beware of is that some newer functions might not apply to an older level of OS/400. Table E.6 lists the GUI functions available as of V4R4 (V3R2M2).

Table E.6: AS/400 Toolbox for Java Graphical Access Classes as of V4R4 (V3R2M2).

Command Call

Data Queues

Error Events

IFS

JDBC

Record-Level Access

Jobs

Messages

Network Print

Permissions

Program Call

System Status

System Values

Users and Groups

These classes are not described here because you might not be writing Java GUIs (instead, you might be writing HTML UIs via Servlets and JSPs). We leave them to your own exploration.

Getting the AS/400 Toolbox for Java Classes

To use the AS/400 Toolbox for Java, you must first get it. If you have V4R2 or later, it is on your system CD-ROM or tape. To install it, use GO LICPGM, select option 11, and install 5769 JC1 (for a Version 4 operating system). This places the files jt400.jar and jt400.zip in your IFS, in the directory /QIBM/ProdData/HTTP/Public/jt400/lib. You don't need both of these files; either one will do. We prefer the .jar file be- cause it is smaller. There is also a jt400doc.zip file, which contains the JavaDoc detailed help for the Toolbox classes in English (jt400mri.zip has the other languages). To see the JavaDoc help, download this file to your workstation and unzip it.

Note

You can use the jar tool in the JDK to unzip a file, as in jar -xvf jt400doc.zip. This gives you many html files, so start with the one named In-dex.htm in the doc_en subdirectory (or Toolbox.htm in the doc subdirectory for later releases). This documentation also is integrated into the online help of both VA Java and CODE/400.

If you prefer to get the very latest version of the Toolbox, go to www.ibm.com/as400/java and follow the AS/400 Toolbox for Java link. The team always has a beta version there of the upcoming release. Alternatively, you can order the latest GA version on CD-ROM for a fee. Or, you can simply get the Toolbox as part of CODE/400, VisualAge for Java Enterprise Edition, or Inprise JBuilder.

If you are going to use the Toolbox classes from Java code running on a client, and you don't have it as part of one of the products already listed, then simply copy the jt400.jar file from the IFS to your workstation. Use any number of methods for this, such as FTP. Once on your workstation, add it to your CLASSPATH. You do not have to unzip this file.

If you are going to use the Toolbox classes for Java code running on the OS/400 itself, you will have to add the /QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar file to your OS/400 CLASSPATH, using one of the methods described earlier in this appendix. As of V4R4, there is also a jt400access.zip file, which is a smaller version containing only the non-visual classes. You probably want to use that on OS/400. This file has also already been optimized for you with the CRTJVAPGM command.

Finally, the Toolbox requires the QUSER user ID so ensure that its password has not expired. Also, be sure to start all the host servers (STRHOSTSVR ALL) and the TCP/IP DDM server (STRTCPSVR SERVER(*DDM)). By the way, ensure you have the TCP/IP Connectivity Utilities for AS/400 installed (5769-TC1).

Using the AS/400 Toolbox for Java Classes

The following discussion is independent of the JDBC classes, which are described in chapter 13 and are the only industry-standard classes in the Toolbox. To write code that uses the AS/400 Toolbox, import the package com.ibm.as400.access. Your first job is to create an instance of the AS400 class. This class will manage a connection to your local or remote AS/400.

You must specify the system name, at a minimum. If you are running this program on OS/400, you can specify "localhost" as this parameter. If you are running this program on a client that will connect to OS/400, you might also want to specify a user ID and password. These will be used to log in to OS/400. If you don't specify these, the user will be prompted for them. For Java running on OS/400, the user ID and password of the current job will be used, but you can specify different values if you want. If you are using the record-level access classes, you must specify a user ID and password, even if running on OS/400 itself:

AS400 mySystem = new AS400("NY400"); // connect to NY400, prompt if on
client
AS400 mySystem = new AS400("NY400","PHIL","NICEGUY");

For a Secure Socket Layer (SSL) connection, use the SecureAS400 subclass instead of AS400. Once you have an AS400 object, it can be used with any of the classes. All the main function classes, like CommandCall and ProgramCall, take an AS400 object as a parameter on their constructor. If no user ID and password are given, and you are running on a client, the user will be prompted for a user ID and password the first time the AS400 object is used by one of these classes.

It is your choice whether to have only a single AS400 object or multiple objects if you expect heavy network traffic or want to support multiple, simultaneous user sessions. When you are done with your OS/400 connection, you should use the method disconnectAllServices to disconnect from OS/400.

The following sections look briefly at a few of the more popular Toolbox classes.

CommandCall

Assuming you have an AS400 object named mySystem already, you can use it to call one or more batch commands on that AS/400. Just instantiate a CommandCall object with the AS400 object as a parameter, and then call the run method for each command to be run. To get the messages that resulted from the command, call the getMessageList method. This returns an array (possibly null) of AS400Message objects, which you can simply walk and call the getText method on:

CommandCall as400cmd = new CommandCall(mySystem);
boolean cmdOK = as400cmd.run("ADDLIBLE MYLIBRARY");
AS400Message msgs[] = as400cmd.getMessageList();
if (msgs != null)
   for (int idx=0; idx<msgs.length; idx++)
      System.out.println("Message: " + msgs[idx].getText());

The run method returns true if the command ran OK.

ProgramCall

Calling any *PGM object on OS/400 is easy. If it doesn't have any parameters, it is very easy. You simply instantiate a ProgramCall object, passing it an AS400 object, and run the remote program using run, specifying at that time the program to run. Afterwards, you can use getMessageList to retrieve the messages sent by the executed program. Here is an example:

ProgramCall as400pgm = new ProgramCall(mySystem);
String pgmName =
   QSYSObjectPathName.toPath("PHIL","PRINTRPT","PGM");
boolean pgmOK = as400pgm.run(pgmName, null);
AS400Message msgs[] = as400pgm.getMessageList();
if (msgs != null)
   for (int idx=0; idx<msgs.length; idx++)
      System.out.println("Message: " + msgs[idx].getText());

The run method returns true if the program ran successfully. We passed in null for the second parameter of the run method because we did not have any parameters in this example.

To pass parameters, you enter into the world of data conversion. To start with, you would need to create an array containing ProgramParameter objects. The array will have as many elements as there are parameters. For example, suppose an OS/400 program to be called takes in a six-digit integer that is a customer ID, and a packed decimal (7.2) parameter that will be updated by the program to hold the current balance for this customer. Start by creating the ProgramParameter array object:

ProgramParameter parms[] = new ProgramParameter[2];

Now, you need to set each element in the array. Do this by instantiating ProgramParameter objects for each element.

This class has different constructors depending on whether you are creating an input parameter, an output parameter, or an input/output parameter. An input parameter requires a byte array for the constructor, an output parameter requires a integer stating the number of bytes required for the returned data, and an input/output parameter requires both.

Let's start with the input parameter. It is going to be a six-digit, zero-decimal numeric value, and let's say the number you want is specified in the Java variable custID. To convert that int value to a byte array for a six-digit numeric value that an OS/400 program will understand, you have to use the AS400Bin4 class (for four-byte numeric data types on OS/400). Since it only takes Integer objects, not int primitives, you first convert the int value to an Integer object (as discussed in chapter 5). All this is shown here:

int custID = 123456;
Integer custIDObj = new Integer(custID);
AS400Bin4 binConverter = new AS400Bin4(); // converting machine!
byte[] custIDData = binConverter.toBytes(custIDObj);
parms[0] = new ProgramParameter(custIDData);

The AS400Bin4 class is used to convert Integer objects into byte arrays that the Toolbox functions can handle. Once you have an AS400Bin4 object, you can use it over and over to do conversions to and from Integers and byte arrays.

Now, let's do the second parameter, the output parameter. This is a BigDecimal object in Java, and a packed decimal field on OS/400. It will have a total length of seven digits, with two decimal points. You only need to specify the total length, in bytes, in a ProgramParm constructor for this one. But what is that length? Use the AS400PackedDecimal converter class to find out. It takes two constructor parameters, the total length in digits and the decimal positions in digits. It also has a nice method called getByteLength, which is just what is needed. Here it is, then:

AS400PackedDecimal parm2Converter = new AS400PackedDecimal(7,2);
parms[1] = new ProgramParameter(parm2Converter.getByteLength());

Now you have a parameter list, so you can call the program. After the call, you will want to extract the value in that second parameter. Here is the revised code to run the program:

boolean pgmOK = as400pgm.run(pgmName, parms);

To get the data out, call the getOutputData method on the ProgramParameter object. This just gives a byte array, which is not very useful. You want a BigDecimal object. To convert it, use the parm2Converter object again, with the toObject method, which takes a byte array and gives a BigDecimal object. However, it is typed as Object, so you have to cast. Here it is:

BigDecimal custBalance = null;
if (pgmOK)
  custBalance=(BigDecimal)parm2Converter.toObject(
    parms[1].getOutputData());

Get it? It's tedious, but not that difficult. The good news is, there are alternatives, such as the PCML language that comes with the Toolbox, and the program call SmartGuide (wizard) that comes with VisualAge for Java Enterprise Edition. The latter generates all this for you.

Another option is to write some wrapper classes of your own to hide some of this. For example, Listing E.1 is a superclass of ProgramParameter that makes working with packed decimal parameters easy. It hides the converter inside it, and just asks its callers to supply the length and decimals in the constructor, call the setInput method for input parameters before the call, then the getOutput method for output parameters after the call.

Listing E.1: A superclass of ProgramParameter.
Start example
import com.ibm.as400.access.*;
import java.math.*;

/**
A class makes it easier to work with BigDecimal parameters on
ProgramCall class
*/
public class ParmBigDecimal extends ProgramParameter
{
   protected AS400PackedDecimal converter;

   /**
   * Constructor
   * @param maximum length in digits, including decimal digits but
      not decimal point
   * @param number of decimal positions in digits
   */
   public ParmBigDecimal(int len, int decs)
   {
      super();
      converter = new AS400PackedDecimal(len, decs);
      try {
         super.setOutputDataLength(converter.getByteLength()); }
      catch (Exception exc) {}
   }

   public void setInput(BigDecimal data)
   {
      try {
         super.setInputData(converter.toBytes(data));
      } catch (Exception exc) {}
   }

   public BigDecimal getOutput()
   {
      return (BigDecimal)converter.toObject(super.getOutputData());
   }
} // end class ParmBigDecimal
End example

There are other data conversion classes besides AS400Bin4 and AS400PackedDecimal. They are listed in Table E.7.

Table E.7: Data Conversion Classes in AS/400 Toolbox for Java.
Toolbox Class | OS/400 Data Type | Java Data Type
AS400Bin2 | Signed two-byte numeric | short
AS400Bin4 | Signed four-byte numeric | int
AS400ByteArray | Hexadecimal, or any type | byte[]
AS400Float4 | Signed four-byte floating point | float
AS400Float8 | Signed eight-byte floating point | double
AS400PackedDecimal | Packed-decimal numeric | BigDecimal in java.math
AS400UnsignedBin2 | Unsigned two-byte numeric | short
AS400UnsignedBin4 | Unsigned four-byte numeric | int
AS400ZonedDecimal | Zoned-decimal numeric | BigDecimal in java.math
AS400Text | Character | String (Unicode)
AS400Array | Array | An array
AS400Structure | Structure | An array

DataQueue

If you are familiar with data queues on OS/400, you know they are an excellent mechanism for inter-process communication (IPC). They are like a mailbox that any program can put mail (messages) into and read mail out of. Readers can access messages in last-in-first-out (LIFO), first-in-first-out (FIFO), or by-key sequence. Readers and writers can communicate via the queue synchronously or asynchronously.

Data queues are heavily used on OS/400, which indicate the usefulness of such a mechanism-and the huge success of their grown-up many-platform cousin, IBM's Message Queue Series ("MQ Series"), which we strongly recommend you take a look at.

Data queues, via the Toolbox class for accessing them from Java, are now more than just a great mechanism for communicating between two programs on OS/400. They become a great mechanism for communicating between a Java program and a COBOL or RPG program, where that Java program can be running anywhere in the world on any machine which has a JVM. For Java code running on a remote machine or client, it is a nice architecture to use a data queue to get at your data, instead of going directly against the database from the client. This allows you a level of indirection and the potential benefit of changing either the client code or the server code at any time, without affecting the other. For example, your server code that writes data to the data queue might be an RPG or COBOL program today, but if you switch it to a Java program tomorrow, all those clients reading the queue will not know the difference, or care. It's that encapsulation idea again-hiding details behind some kind of wall.

To use data queues from Java, using the Toolbox, use the DataQueue and KeyedDataQueue classes. The former is for sequential (LIFO or FIFO) access, the latter for reading and writing data by a key. Both classes extend, and hence inherit much of their behavior (methods), from the BaseDataQueue class.

Let's consider an example that uses a sequential data queue with a typical "design pattern" for data queue usage: a client and a server are communicating using two data queues. The first is an input data queue, where the client puts in a request for some type of function. The server program is monitoring this data queue for data (by reading, and waiting forever on that read). Once a request is put in by the client, the server reads it, processes it, probably by reading one or more database files, and then puts the result into the second data queue, where the client, by now, is waiting. Usually, the output is one or more records of information. Let's assume the input queue exists in library ALIB and is called INPQ. The output queue is also in ALIB and is called OUTQ.

As with CommandCall and ProgramCall, you instantiate an instance of DataQueue and pass to it an AS400 object. However, for data queues, you must also pass the name of the queue, as in "/QSYS.LIB/ALIB.LIB/INPQ.DTAQ." Let's use the toPath method in QSYSObjectPathName again to create this IFS-style name. Start with the preliminaries, creating the DataQueue objects, and for good measure, clearing them of any leftover data:

String inpqName = QSYSObjectPathName.toPath("ALIB","INPQ","DTAQ");
String outqName = QSYSObjectPathName.toPath("ALIB","OUTQ","DTAQ");
DataQueue inpq = new DataQueue(mySystem,inpqName); DataQueue outq =
new DataQueue(mySystem,outqName);
inpq.clear();
outq.clear();

Now it's time to communicate. As you know, a good relationship requires good communication, and you want a good relationship with that RPG or COBOL program running on the server (AS/400). By the way, we assume that program was already started, perhaps by using the CommandCall class. (You have to use the SBMJOB command. If you call the program directly, the call won't return because the program will be waiting on the data queue. Alternatively, you could use the ProgramCall class, but do so in a separate thread.)

Let's assume that the client wants to tell the server to retrieve a customer information record. This requires you to put a one-character field onto the queue, with the letter C, which the server program is designed to interpret as "read a customer record." Assume you also have to put a six-digit numeric value, indicating the customer ID. The program on the server will then put onto the output queue a record of information including a result code, the customer ID again, the customer name (as a 30-character string), the customer phone number (as a 15-character string, to be safe), and the customer account balance, which is a packed decimal number (9, 2). This means you need the record formats described in Tables E.8 for the input, and E.9 for the output.

Table E.8: Input Data Queue Format.
Queue Position | Description | AS400 Data Type | Java Data Type
1-1 | Request Type | One character | String
2-7 | Customer ID | 6.0 zoned decimal | int

To write to a DataQueue object, you would use the write method, which requires an entire array of bytes. You can't write individual fields, only the entire record. This means you need to use the conversion classes to convert each field to a byte array, and then concatenate all the byte arrays for all the fields together.

Table E.9: Output Data Queue Format.
Queue Position | Description | AS400 Data Type | Java Data Type
1-1 | Return Code | One character | String
2-2 | Customer ID | 6.0 zoned decimal | int
3-32 | Customer Name | 30 characters | String
33-47 | Customer Phone | 15 characters | String Number
48-56 | Customer Account Balance | 9.2 packed decimal | BigDecimal

To read from the queue, you would use the read method, which returns an object of a class called DataQueueEntry. To get the data out of it, you would use the getData method, which just gives an entire array of bytes. It is up to you to know how to partition this into fields and use the appropriate conversion classes to convert those fields to data. This is possible, but way too much work! The Toolbox team recognized this, so it provided some help.

To work effectively with an entire record of individual fields, you can use a combination of classes from the Toolbox. First, there are field-description classes whose objects are wrapper conversion class objects, much like the wrapper class in Listing E.1. They also allow you to name each field, which comes in handy. There is one field description class for each of the DDS data types on OS/400, and they all take an instance of the corresponding conversion class, plus a field name of your choosing, in their constructor. Table E.10 lists all of the field description classes.

Table E.10: Field Description Classes in AS/400 Toolbox for Java.
OS/400 Field Type | Field Description Class | Convert Class | Java Type
 | FieldDescription: base class |  |
Binary 4 | BinaryFieldDescription | AS400Bin2 | short
Binary 8 | BinaryFieldDescription | AS400Bin4 | int
Character | CharacterFieldDescription | AS400Text | String
Date | DateFieldDescription | AS400Text | String
DBCS Graphic | DBCSGraphicFieldDescription | AS400Text | String
Float 4 | FloatFieldDescription | AS400Float4 | float
Float 8 | FloatFieldDescription | AS400Float8 | double
Hexadecimal | HexFieldDescription | AS400ByteArray | byte[]
Packed-decimal | PackedDecimalFieldDescription | AS400PackedDecimal | BigDecimal
Time | TimeDecimalFieldDescription | AS400Text | String
Timestamp | TimestampFieldDescription | AS400Text | String

In Table E.10, keep in mind that the primitive types in the Java Type column must be wrapped in an object of their class wrappers, such as Integer for int. These field description classes are only remotely interesting by themselves. They become very interesting, however, when you group instances of them together to define an entire record, such as the format of the record you want to put on the input queue, and the format of the record you want to read from the output queue.

The Toolbox supplies a class that allows you to do just this, called appropriately enough, the RecordFormat class. You can create a RecordFormat object and then place a field description object in it (via the addFieldDescription method) for each of the fields in your record layout. This is done here for the example input and output data queues, so you know what the formats for each look like:

RecordFormat inpqFmt = new RecordFormat();
inpqFmt.addFieldDescription(
   new CharacterFieldDescription(new AS400Text(1),"request"));
inpqFmt.addFieldDescription(
   new BinaryFieldDescription(new AS400Bin4(), "custID"));

RecordFormat outqFmt = new RecordFormat();
outqFmt.addFieldDescription(
   new CharacterFieldDescription(new AS400Text(1),"returnCode"));
outqFmt.addFieldDescription(
   new BinaryFieldDescription(new AS400Bin4(), "custID"));
outqFmt.addFieldDescription(
   new CharacterFieldDescription(new AS400Text(30),"custName"));
outqFmt.addFieldDescription(
   new CharacterFieldDescription(new AS400Text(15),"custPhone"));
outqFmt.addFieldDescription(
   new PackedDecimalFieldDescription(new
AS400PackedDecimal(9,2),"custBal"));

This is just like using physical-file DDS to define the fields inside a record format. Just as you have to compile physical-file DDS to read and write records of actual data, in the Toolbox, you have to instantiate an instance of something new-a Record object-to read and write actual data that conforms to the RecordFormat layout.

Let's start by creating a Record object to hold the data to write to the data queue, using the inpqFmt RecordFormat object from above:

Record inpqData = new Record(inpqFmt);
Integer custID = new Integer(123456); // ID of customer to retrieve
inpqData.setField("request",  "C");   // Type of request ("C"ustomer)
inpqData.setField("custID", custID);  // Set ID of customer to
retrieve
inpq.write(inpqData.getContents());   // Put data record on the queue

Notice the use of the record format object in the constructor of the Record object. You would subsequently use the setField method on the Record object to put each field's value into the record. The first parameter of setField is the field name given to the field in the xxxFieldDescription constructor. Alternatively, you could specify a zero-based number representing the field position in the record format. The second parameter is the object that is the value for that field. Finally, the getContents method is used on the Record object to return a single byte array containing all the field values, which is placed on the data queue using the write method of the input DataQueue object.

Now you finally have data on the input queue, which should awaken the host program (not shown) to read the database and put the results into our output queue. The Java client code now needs to read the results from that output data queue. To do this, simply use the read method on the outq DataQueue object, which returns an object of type DataQueueEntry. This class is just a thin wrapper for a byte array, and not very interesting by itself. You really want to get the data into a Record object that uses the outqFmt RecordFormat class, so you can easily retrieve individual fields using getField from the Record class. You can do this using the getData method of the DataQueueEntry object, which gives a byte array. This byte array can then be used to create a populated Record object, by passing it into the constructor of the Record class along with the RecordFormat object:

Record outqData = new Record(outqFmt, outq.read(-1).getData());
String     outReturnCode  = (String)outqData.getField("returnCode");
Integer    outCustID      = (Integer)outqData.getField("custID");
String     outCustName    = (String)outqData.getField("custName");
String     outCustPhone   = (String)outqData.getField("custPhone");
BigDecimal outCustBalance = (BigDecimal)outqData.getField("custBal");

The first thing this code does is create a new Record object by passing the output queue RecordFormat object created earlier, and the byte array read from the output queue. To get that byte array, it had to call read to get the DataQueueEntry object, and then getData on that to get the actual byte array. Finally, it simply calls getField for each field in the record format, specifying the name given to each field. Notice the result of getField has to be cast to the appropriate target type.

What happens if the call to read on the output queue happens before the host program has done its work and put the result on the output queue? Nothing. The program will simply wait until data is put on the queue before returning from the read call. This is because -1 was passed for the parameter to read. Alternatively, a positive integer could have been passed, representing the number of seconds to wait.

While we have not shown it for simplicity, many of the methods called in this data-queue example throw exceptions. You will have to place them inside try/catch statements. To see the full example, refer to the file TestDataQueue.java on the supplied CD-ROM.

The RecordFormat and Record classes introduced here are very handy, and can also be used with the ProgramCall class. This is very useful when the program you call returns a record of information. They are also used when doing direct record access to the database, as you will see next.

Record-Level Database Access

Now that you have seen the basics of the AS/400 Toolbox for Java, this introduction wraps up with a look at a way to directly access your DB2/400 data. This is an alternative to using JDBC or SQL/J as presented in chapter 13.

The Toolbox gives you classes for working with your DB2/400 database files directly, a record at a time, as you are used to in COBOL. The classes are AS400File, which is the parent class, the KeyedFile child class for files accessed by key, and the SequentialFile child class for files accessed sequentially. The parent class AS400File offers a lot of functionality for the management of files, such as creating and deleting them, locking them, and managing commitment control on them. This functionality is not covered here, but the Toolbox documentation does cover it, if you need it. This section just focuses on using the KeyedFile class to read and write an existing keyed file.

The steps to working with data in an existing DB2/400 file are a superset of what you have seen for calling commands and programs and accessing a data queue:

  1. Create an AS400 object, as usual.

  2. Create a KeyedFile (or SequentialFile) object, passing the AS400 object as a parameter and identifying the name of the DB2/400 file using IFS-style syntax.

  3. Identify to the Toolbox the layout of the records in the file, by associating a RecordFormat object with the file object.

  4. Open the file, specifying if it is to be accessed read-only, write-only, or both, as well as the record blocking factor to use (ignored for read-write access) and the commitment control to use.

  5. Optionally, use the appropriate methods to position the cursor.

  6. Use the appropriate read methods to read a record (a Record object) from the database.

  7. Use the appropriate methods to add, update, or delete records in the database.

  8. Close the file object.

Let's look a little closer at these steps. As you can see, the RecordFormat and Record classes play a prominent role. This is not surprising, as DB2/400 record-level access works with records of data, and those records need to be defined field by field. However, the great thing about the record-level access functionality of the Toolbox is that you do not have to define the RecordFormat by hand, unless you really want to. Rather, the Tool-box supplies another class to create these objects for you, the AS400FileRecordDescription class, based on the existing database record format itself.

You have two options for creating a RecordFormat object directly from an existing database. You can do it every time, dynamically, as your program runs, or once, statically, at development time, and then reuse the generated class at runtime. In both cases, you first have to instantiate an instance of the AS400FileRecordDescription class, passing in an AS400 object and the IFS version of the file name. Again, you can get the latter using the static method toPath of the QSYSObjectPathName class, but for files you use the four-parameter version. This is shown here:

AS400 mySystem = new AS400("MYSYSTEM");
String fileName =
QSYSObjectPathName.toPath("MYLIB","MYFILE","MYFILE","MBR");
AS400FileRecordDescription frd =
new AS400FileRecordDescription(mySystem, fileName);

Note that the third parameter to toPath is the member name of the file to access, and the fourth parameter is always MBR, representing the type of object.

This dynamic retrieval of a record format is done by calling the method retrieveRecord Format, which returns a populated RecordFormat object:

RecordFormat[] myFileFmt = frd.retrieveRecordFormat();

This method returns an array because of the possibility that the file is a multiple-record-format logical file. In most cases, you will only be interested in the first entry in this array. This is very nice, but if you want to squeeze more performance out of your application, it is best to use this just once and save the result in a class that extends RecordFormat. The Toolbox allows for this by using its createRecordFormatSource method on an AS400FileRecordDescription object:

frd.createRecordFormatSource(null, null);

This creates a .java source file in the current directory, which you can subsequently compile via javac and reuse time and again. The file name will be the name of the record format in the database file with the term Format appended. (You get multiple .java files for multiple-record-format logical files.) The two parameters the method takes are a target directory to place the resulting java file(s) in, and the name of the package to use to generate package statements in the generated files. These default to "current directory" and "no package," respectively, if null is passed.

To use the generated file (after compiling it), simply instantiate an instance of it. For example, if the file above contained a record format named RECORD1, this is how to use the output (after compiling it with javac):

RecordFormat myFileFmt = new RECORD1Format();

Once you have a RecordFormat object, it is time to call the setRecordFormat method on the KeyedFile or SequentialFile object, and then call open on that object.

Let's look at an example that reads a keyed database file on OS/400. Assume the file CUSTOMER exists already, and holds customer information data keyed by Customer ID. The "on-the-fly" method is used to create the RecordFormat object here:

AS400 mySystem = new AS400("MYSYSTEM");
String fileName =
QSYSObjectPathName.toPath("CLIB","CUST","CUST","MBR");
AS400FileRecordDescription frd =
   new AS400FileRecordDescription(mySystem, fileName);
RecordFormat[] custFileFmt = frd.retrieveRecordFormat();
KeyedFile custFile = new KeyedFile(mySystem, fileName);
custFile.setRecordFormat(custFileFmt[0]);
custFile.open(AS400File.READ_ONLY, 100,
AS400File.COMMIT_LOCK_LEVEL_NONE);

This creates a KeyedFile object, tells the Toolbox what the record format for it is (which we queried dynamically from the Toolbox), and opens the file. To open the file, it uses constants defined in the parent class AS400File to specify whether it is to be accessed by READ, READ_WRITE, or WRITE_ONLY, and what the commit level is. It also specifies in the second parameter to open what the blocking factor is, which in this case is set to 100 records.

Now that the file is open, it is time to read from it. This is done by simply calling the read method on the KeyedFile object, and specifying the key to use. The key is an Object array, since many files have multiple fields defined for the key. The objects you put in the array are instances of Java classes that are compatible with the data type of the key fields in the database, for example, Integer, String, or BigDecimal. Let's assume the file is keyed only by the customer ID field, which is a four-byte numeric field. Here is the code to read a particular customer record:

Object[] keys = new Object[1];
keys[0] = new Integer(123456);
Record custData = custFile.read(keys);

Notice that the read method returns a Record object (or null if no record is found). Recall that getting field data from a Record object is simply a matter of calling the getField method on that object. The getField method expects, as input, either the name of the field, which in this case is the uppercase field name from the file itself, or the zero-based field-position number within the record. The following code reads each field from the retrieved record, using the latter method:

if (custData != null)
{
   Integer    outCustID      = (Integer)custData.getField(0);
   String     outCustName    = (String)custData.getField(1);
   String     outCustPhone   = (String)custData.getField(2);
   BigDecimal outCustBalance = (BigDecimal)custData.getField(3);
}

Using the field-position number is more efficient, but also less resilient to change if fields are added, removed, or repositioned in the record format.

When you are done accessing the file, take care to close it using the close method on the file object.

There are alternatives to the read method, which just retrieves the first record matching the given key (or partial key). These are readAfter and readBefore, which return the first record after or before the record matching the given key. Subsequent reads use other methods, which are relative to the current cursor position: readNextEqual or read PreviousEqual. These do not take a key array, due to their relative nature.

You might also want to position the cursor without doing that initial read. This is possible via the methods positionCursor, positionCursorAfter, and positionCursorBefore. These all take the key array like read, readNext, and readPrevious, but they don't have the overhead of doing an actual database record retrieval. Finally, the read, readNext, readPrevious, positionCursor, positionCursorAfter, and positionCursorBefore methods all allow an optional second parameter, which is a constant from the KeyedFile class. These constants allow you to specify matching criteria for the key, versus just accepting the default ("equal"). The constants are KEY_EQ, KEY_GT, KEY_GE, KEY_LT, and KEY_LE. These are self-explanatory.

Suppose you have a keyed file and want to retrieve all the records in it, ordered by key. This common requirement is done by using the KeyedFile class, and the readNext method with no parameter. This returns a populated Record object as long as there is another record, and null when you are at end of file. You don't have to specify any key objects in this case; the database will be read by key, by default.

Of course, your applications don't always just read data. Sometimes, they have to update it as well. This, of course, is possible via the Toolbox. Just be sure to specify AS400File.READ_WRITE or AS400File.WRITE_ONLY on the open method call. Then, you can use the update or deleteRecord methods to update or delete records identified by the usual array of key objects. The update method also needs a Record object containing the new data to put in the fields. Those KEY_XX constants are also allowed on these method calls. Here is an example of updating the previously read custData record:

custData.setField("CUSTNAME", "ABC Company");
custFile.update(keys, custData);

Of course, most of the methods shown here throw exceptions, and so they will have to be wrapped in try/catch statements. A full example of reading the fictitious keyed customer file is in the TestKeyedRecordAccess.java file on the supplied CD-ROM.

You should find that in many cases these record-level access classes offer performance gains over the JDBC classes, except for applications that can exploit the expressive power of SQL.

Other AS/400 Toolbox for Java Functionality

In addition to the non-visual and visual classes, the Toolbox comes with a number of utility programs, such as JarMaker, which can strip the Toolbox jar file down to a bare minimum if you don't need all the functions in it. This is very important for applets because you want to reduce the download time.

The Toolbox also comes with support for a language called PCML (Program Call MetaLanguage). This is an XML-based language that allows you to very easily describe a program call in terms of the parameter types it expects. Then, you use this to call the program from your Java application. This is much easier than the relatively painful ProgramCall class, but compiles into the same thing.


VisualAge For Java Enterprise Toolkit For AS/400

This appendix concludes with a brief description of the IBM VisualAge for Java product. Specifically, we discuss what it has to offer OS/400 Java programmers.

When OS/400 programmers write Java code, it is for one of two possibilities: to run on a client connected to OS/400 (perhaps via the AS/400 Toolbox for Java) or to run on OS/400 itself. The Enterprise Toolkit for AS/400 (ET/400) in VisualAge for Java offers tooling to address both these needs. ET/400 is only available in the Enterprise Edition of VisualAge for Java. Table E.11 shows the tools available for both intentions, as of Version 3.0.

Table E.11: VisualAge for Java ET/400 Support for OS/400 Programmers.
Feature | Client | OS/400 | Description
AS/400 Toolbox | Yes | Yes | The Toolbox is pre-imported into VAJava, and for Java has its own palette in the VCE.
Program Call SmartGuide | Yes | Yes | This wizard generates Java code to call any program on OS/400. It generates Toolbox ProgramCall code.
Display File Conversion SmartGuide | Yes | No | This wizard imports a display file object and converts it to a Java AWT or Swing GUI.
Subfile SmartGuide | Yes | No | Given an OS/400 database file, this wizard generates a Java GUI subfile for listing the contents of the file, including the code to populate it.
AS/400 Entry Field Java Beans | Yes | No | These Java beans for "smart" GUI entry fields support data types, range/values/comparison checking, edit code or edit words, and auto-advance capability.
Object List Java Beans | Yes | No | These GUI Java beans display objects on OS/400, supporting the same generic name filtering as WRKOBJPDM does.
Export to AS/400 | No | Yes | This exports selected packages or classes in the VAJava IDE to OS/400 IFS. The target location is specified once in the properties and reused and shared by all in the team.
Remote Run | No | Yes | This runs the class selected in the IDE remotely on OS/400 by invoking the RUNJVA command. Options are specified once and reused and shared by all in the team. Anything written to standard out by System.out.println is captured and displayed in the local console window.
Remote Compile | No | Yes | This compiles the class selected in the IDE remotely on OS/400 by invoking the CRTJVAPGM command. Options are specified once and reused and shared.
Remote Debug | No | Yes | This debugs the class selected in the IDE remotely on OS/400 using the same debugger that is in CODE/400. Options are specified once and reused and shared.

Although this information is valid as of Version 3.0 of VisualAge for Java, each release brings more functionality. This support is added to the VisualAge for Java product by the same team in IBM Toronto that brings you all the traditional OS/400 languages and tools, such as the COBOL compiler, SDA, SEU, PDM, CODE/400, and VisualAge RPG. Of course, all the basic support in VAJava for all Java programmers is available to OS/400 Java programmers as well.

For more information on this functionality, refer to the Getting Started manual of the Enterprise Edition of VisualAge for Java, or go to www.ibm.com/software/ad/vajava and use the VisualAge Developer's Domain link.

Common Connector Framework in VisualAge for Java

Finally, if you happen to use CICS, Encina, MQSeries, Host On Demand, or SAP R/3, and want to access these from Java, VisualAge for Java includes a number of "connectors" to make it easy. This includes tooling called the Enterprise Access Builder that conveniently generates all the code needed to access all or any of these in a consistent fashion. Refer to the sections on Common Connector Framework and Enterprise Access Builder in appendix D for more information. Although appendix D discusses these concepts in relation to OS/390, they apply to OS/400 customers as well. Watch for specific documentation on how to use these for OS/400 Java applications at the AS/400 Web site, www.ibm.com/as400/java.

There are other Access Builders in VisualAge for Java that might be of value to you. For example, if you use Lotus Domino, you can use the Access Builder for Domino to access your Lotus database from Java.


Appendix F: Software Loading Instructions

Overview

All of the code samples shown in the sample listings in this book are available on the accompanying CD-ROM. To get them, insert the CD and cancel the autostart installation process that it will invoke. Then use Windows Explorer or the DOS Prompt window to copy the contents, including subdirectories, of the \bookj4c\SampleSrc directory to your PC. There is a subdirectory for each chapter and appendix in the book. Inside each directory, a readme.txt file maps the files names of the source files in that directory to the "Listing x-y" listings from this book.

The sample source is also available at the Web site http://www.midrangecomputing.com/books/JavaCOBOL.cfm .


To-Do List Swing GUI Example

Chapter 12 of this book discusses the Abstract Window Toolkit, or AWT, at some length. This is the original Java GUI (graphical user interface) package. That chapter gives only a brief introduction to the relatively new GUI package called Swing. If you are interested in learning more about Swing, the CD-ROM includes a reasonably complete little application written with it. The intention is that you can look at the application and find the functionality you are interested in writing, and then copy the code for that functionality from the source, which is also supplied.

The little application is a to-do list manager. It allows you to create and maintain files that have to-do items. You might actually find this application useful in its own right, as well as serving as a Java Swing coding sample.

Note

The tools list example in Swing is written for JDK 1.2.0.

To install the To-Dos application, create a directory called ToDos, off (say) your D: drive. Then, copy the following files to this directory from the CD-ROM (or from your hard drive if you have copied the \bookj4c\SampleSrc directory tree):

  • \bookj4c\SampleSrc\ch12\ToDos\ToDos.jar

  • \bookj4c\SampleSrc\ch12\ToDos\ToDos.bat

Note

If your directory for the To-Dos application is not d:\ToDos, you will need to edit the ToDos.bat file to reflect the correct directory.

To run this application, simply run ToDos.bat. You might want to create a shortcut to it on your desktop if you decide to use it regularly. Figure F.1 shows what the application looks like. For information about the code behind the application, see the readme.txt file in the \bookj4c\SampleSrc\ch12\ToDos directory.

Click To expand
Figure F.1: A supplied sample application for managing to-do items.


VisualAge For Java On Your CD

Also on the CD-ROM is a complementary trial copy of IBM's VisualAge for Java, Entry Enterprise Edition, version 3.0. This is a not-for-resale copy of the product. It is only for your personal use. No support is included with the product, but information on the product and support options, including a question-and-answer database, can be found at www.ibm.com/software/ad/vajava.

To install VisualAge for Java, insert the CD-ROM into your computer's CD-ROM drive. This will begin the installation process. Simply follow the prompts, taking the defaults.

Please note that while VisualAge for Java is included so you can investigate using it as your Java development tool, the samples were all written using a basic editor and the JDK command-line tools. We recommend you also start this way, and then learn VisualAge for Java after you are comfortable with the Java language. The editors we recommend for your learning phase are VisualAge for COBOL for OS/390 programmers, and CODE/400 for OS/400 programmers. Trial versions of these (which are all you need for Java coding) are available from www.ibm.com/software/ad/vacobol, and www.ibm.com/software/ad/varpg , respectively.


 Team-Fly

Index

Symbols

!, 107-109
/*, 80-88
//, 80-82
" ", 244, 245
;, 34, 80, 93
&&, 107-109
&, 107-109
+, 242-245, 249
= =, 95, 107-109
=, 37, 95, 101-103, 119
{ }, 33-34, 80, 93, 173
[ ], 200-201, 206-207
| |, 107-109


 Team-Fly

Index

A

abs, 118
abstract classes in Java, 343-345, 348, 371
abstract methods in Java, 342-343
Abstract Windowing Toolkit (AWT) for Java user
interface (See also Swing; user interface for Java), 438-439, 440-504
OS/390 and Java, 703-704
OS/400 and Java, 736-737
packages for, 6
Visual Composition Editor (VCE) for, 652-654,653
append method, in, 440, 483
applets and, 494-499
background colors in, 443, 446
basic components in, 441-442, 442
blank window in, 447
BorderLayout manager in, 450, 451-453, 453
Buttons, 440, 441-442, 482
CardLayout manager in, 450
Checkboxes, 440, 441-442, 482
Choice, 441-442, 482
classes in, 440
code added to objects in, 477-478, 478
colors in, 443-444, 446
constraints for GridBagLayout manager, 457-458
containers, 440, 445
context menus in, 469
country-specific formatting for, 499-504
Dialog in, 440, 445, 448-448, 449
error checking in, 490-492
error messages in, 492-494
event model in, 473-474
events in, 440, 473-481
extending Frame class in, 446, 451-452
FlowLayout manager in, 450, 453-454, 454
focus of objects and, 443
font selection, 443, 446
foreground colors in, 443, 446
formatting panel areas, 465-466
Frame class, 446, 451-452
frames, 440, 445
get methods for input processing in, 482-484
getCheckboxGroup, 482
getItem, 483
getLabel, 482, 483
getSelectedCheckbox, 482
getSelectedIndex, 482, 483
getSelectedItem, 483
getSelectedText, 482, 483
getState, 482
getText, 482, 483
graphics in, 499
GridBagLayout manager in, 450, 456-461, 459, 460
GridLayout manager in, 450, 454-456, 456
hypertext markup language (HTML) and, 498-499, 498
initialization of objects in, 441-442
input processing in, 482-484
insert method, 483
instantiation of objects in, 441-442
isEnabled, 443
isVisible, 443
justifying/padding integer data in, 491-492
KeyListener for, 483, 491
Labels, 441-442, 482
language-specific formatting for, 499-504
layout managers for, 440, 449-467
listeners for events in, 475-476
Lists, 441-442, 483
locales for, 499-504
Menu for, 467-469
MenuBar for, 467-469
MenuItem for, 467-469, 482, 483
messages to users in, 476-477, 492-494
methods shared by, 443
overriding methods in applets in, 496-497
pack method in, 452-453, 453, 454, 454, 455, 456, 460-461, 460
padding between components in, 461, 461
Panels in, 440, 445, 462-467
pixels to size in, 445
placing objects in panels, 463-464 , 464, 466-467 , 466
PopupMenu in, 469-471 , 471
positioning of objects in (layout managers) , 449-467
positioning windows in, 445
push button in, 441 , 441
remove method, 483
removeAll, 483
replaceItem, 483
replaceRange, 483
requestFocus, 443
ResourceBundle subclass for locales in, 501-504
select method for, 483 , 483
selectAll, 483
set methods for input processing in, 482-484
set Title, 445
setBackground, 443 , 446
setCaretPosition, 483
setEditable, 483
setEnable, 443
setFont, 443 , 444 , 446
setForeground, 443 , 446
setLabel, 482 , 483
setLocation, 445
setSelectedCheckbox, 482
setSize, 445
setState, 482
setText, 482 , 483
setVisibile, 473
setVisible, 443
shortcuts for menu items, 472-473 , 473
sizing of objects in (layout managers), 445 , 449-467
Swing vs., 504-506
TestEvents class for, 480-481
TestFrame, 447
TextArea, 441-442 , 483
TextField, 441-442 , 483 , 490
title of windows in, 445
To-Do List example, 484-490 , 484
transferFocus, 443
weighting in GridBagLayout manager, 461 , 461
WindowListener interface in, 479-481
ACCEPT, 282 , 293
ACCEPT DATE, 279-280
Access Builder for Domino, for OS/400 and Java, 760
access control (See database access in COBOL; database access in Java)
ACCESS MODE, 520
access rights to names in COBOL, 64
access rights to names in Java, 63-66
accessibility of Java, 4
accessors (getters) in Java, 66
acos, 118
activation groups in COBOL, 674
Ada, 8
ADD, 101 , 104
Add Binding Directory Entry (ADDBNDDIRE) in , 676
add method, 299-300 , 440 , 483
ADD...ON SIZE ERROR, 90
ADD-DURATION, 289 , 298
addElement method in Java vectors, 222
addition (See operators in COBOL; operators in Java )
after method, 297
AIX, Java support in, 6 , 656
ALL, 181
allocating Java classes, 36-37
alphabetic data item in COBOL, 162
alphanumeric data item in COBOL, 162
alphanumeric-edited data item in COBOL, 162
AND, 90 , 107-109
AND, bitwise operators in Java, 110-112
angle brackets in Java classes, 33
angle function, 119
anonymous inner classes in Java, 588-590
append method, 268 , 483
applets in Java, 3 , 16-17 , 22 , 494-499
distribution of, 57
Java user interface and, 494-499 , 505
JavaKey tool to sign, 495
overriding methods in, 496-497
packages for, 602
sandbox for, 17 , 495
signatures, digital signatures for, 17
Application Development Manager (ADM) for
COBOL, 677
Application Programming Interfaces (APIs)
in COBOL database access and, 516 , 517
in SQL, 528-529
applications in Java, 22
distribution of, 57
arithmetic expressions in COBOL, 90
arithmetic operators in COBOL, 100-101
arithmetic operators in Java, 100-101
Array class in Java, 200 , 218 , 239
array index expression in Java, 116
arrays in Java (See tables in COBOL), 21 , 198-220
Array class for, 200 , 218 , 239
array index expression in, 116
binarySearch method, 218 , 220
brackets to indicate dimensions in, 200-201 , 206-207
capacity of StringBuffer objects, 269-271
casting contents of, 212-213 , 363-365
classes for, 215-217
Comparable interface variable for sorting, 219-220
compile-time arrays in, 203-204 , 239
compile-time initialization of, 203-204
copying contents of, 212-213
creating, 202-203
declaring, 200-201
defining length of (See also vectors), 199-200 , 205-206 , 221
dimensions in, 200
elements in, 199 , 206
enumerating elements of, 360-363
equals method, 218
fill method, 218
hashtables and, 199 , 229-333 , 230
helper methods for, 217-222
initialization of, 203-210
instantiating, 202-203
interfaces to search, 352-356
left-to-right rule in, 213-214 , 239
length of, defining (See also vectors), 199-200 , 205-206 , 221
multidimensional arrays in, 206-210 , 239
naming, 200
objects as, 211-213 , 239
one-dimensional, 200
parameter passing in, 233-238 , 239
pass-by-reference for parameters in, 234-237
pass-by-value for parameters in, 233-237 , 239
references to, 211-213
runtime initialization of, 204-206 , 208 , 239
search algorithm for, 217-222
sorting, 219-220
SQL database access, 572
swap method for, 238
three-dimensional, 209-210 , 210
two-dimensional, 201 , 207-208
unbalanced, 213-214 , 239
using arrays instead of primitives to pass by reference, 237-238
vectors in, 199 , 221-229
zero-based nature of, 205
AS/400 Developer Kit for Java in OS/400 , 727 , 728-729
AS/400 Toolbox for Java (See also OS/400 and Java) , 728 , 737-758
calls in, 738
command call in, 738
CommandCall class in, 742 , 743
conversion classes for, 746 , 747 , 746
data access in, 738
data areas in, 738
data queues in, 738
data types in, 740
DataQueue class in, 747-750
digital certificates in, 739
exceptions in, 740
field description classes in, 750-751
helper classes, 739-740
Integrated file system (IFS) and, 738
Java functions in, 738-739
JDBC drivers in, 738
jobs in, 739
messages in, 740
network printing in, 739
permissions in, 739
Program Call MetaLanguage (PCML) for, 757
program calls in, 738
ProgramCall class in, 742 , 743-746
ProgramParameter in, 744-746
QSYS file system in, 740
Record class in, 754
record formats in, 740
RecordFormat class in, 754
record-level access in, 738 , 753-758
Swing GUI components in, 740-741 , 740
system name for, 742
system status in, 739
system values in, 739
trace in, 740
user IDs for, 742
user space in, 738
users and groups in, 739
AS400 object in OS/400, 739
asin, 118
assignment operators in COBOL, 101-103 , 119
assignment operators in Java, 37 , 91 , 95 , 101-103 , 119
assistive technology with Java user interface, 505
asynchronous processing and threads, 395-396 , 396,401-405 , 405-407
AT END, 122 , 379
AT INVALIDKEY, 379
atan, 118
atan2, 118
automatic data items in COBOL, 30


 Team-Fly

Index

B

background color in Java user interface, 443 , 446
base class, 322
Basic, 3
basic sequential access method (BSAM), COBOL
database access and, 516
batch processing in Java for in OS/390 , 703
batch updates in COBOL, 22
batch updates in SQL, 574-575
bean-managed persistence (BMP) in Enterprise Java
Beans (EJBs), 643
beans (See enterprise Java beans; Java beans)
before method, 297
BigDecimal data type in Java, 166 , 184-187 , 195 , 196 , 247 , 266
BINARY COBOL data type, 166
binary large objects (BLOB) in SQL database access, 572
binary operators in COBOL, 90 , 99 , 100-101
binary operators in Java, 99 , 100-101 , 100
binarySearch method for Java arrays, 218 , 220
binding directories in COBOL, 676
binding in COBOL, 26 , 26, 674-675
bitwise operators in Java, 110-112
blocks in Java, 33 , 80 , 91 , 93-95 , 173
Boolean class in Java, 192-194
BOOLEAN COBOL data type, 167
boolean data item in COBOL, 163
boolean data type in Java, 165 , 167 , 181 , 187-188 , 195 , 197
Boolean values (See also logical operators)
BorderLayout manager in Java user interface, 450 , 451-453 , 453
bound properties of Java beans, 622
braces to delimit blocks in Java, 33-34 , 80 , 93 , 173
brackets to indicate dimensions in Java arrays , 200-201 , 206-207
break statement, 96-97 , 122 , 150-155 , 160
browsers, class browsers for Java, 327 , 327, 327
business objects in object-oriented programming , 372-373 , 376
Button, Java user interface, 440 , 441-442 , 482 , 506
BY CONTENT, 48
BY REFERENCE, 235
BY VALUE, 48 , 235
Byte class in Java, 192-194 , 192
byte data type in Java, 164 , 165 , 197
bytecode for Java, 4 , 7-8 , 7, 22


 Team-Fly

Index

C

C language, 8
C++, 8
Calendar class, 295-296
CALL, 27 , 29 , 44 , 47 , 72 , 122 , 172 , 659 , 659, 663 , 671 , 675
call level interfaces (CLIs), 516 , 528-529
CALL LINKAGE TYPE, 672
CALL...USING COBOL, 29
CallableStatement, 537 , 556 , 558-559
callbacks, Java interfaces as, 352-356
calls and calling
COBOL, 27
methods in Java, 45-46 , 46, 116
OS/400 and Java, 738 ,
programs and commands into Java 615-617
programs in COBOL, 44-45 , 46, 69
programs in Java with Java Native Interface (JNI),
591-593 , 615-617
programs in Java, 69-71
synchronous vs. asynchronous, 395-396 , 396
CANCEL, 57
capacity of StringBuffer objects, 269-271
capacity method in Java vectors, 222
CardLayout manager in Java user interface, 450
caret operator, 107-109
cascading style sheets for DHTML, 19
case statement in Java, 131-132
case-sensitivity of Java, 45 , 170
casting, 363-365
arrays in Java, 212-213
data types in Java and COBOL, 188-192 , 195
catch for Java exceptions, 388-391
categories of data items in COBOL, 162-163
CD-ROM, software loading instructions, 761-763
CEEHDLR API for COBOL exception handling, 378
CEEHDLU API for COBOL exception handling, 378
ceil, 118
char/character data type in Java (See also String; strings in COBOL), 165 , 166 , 180 , 195 , 197
escape sequences for, 180
Unicode standards for, 165 , 195 , 259
Character class in Java, 192-194
character data items in COBOL (See strings in COBOL)
character large objects (CLOB) in Java/SQL database
access, 572
charAt method, 262
Checkbox in Java user interface and, 440 , 441-442 , 482 , 506
child Java classes, 322-323 , 331 , 374
Choice in Java user interface, 441-442 , 482
CICS (See Customer Information Control System)
CL commands for Java, in OS/400, 735-736
CLASS CONDITION, 126
class condition test in COBOL strings, 264-265
class instance creation expressions in Java, 91
class methods in Java, 61
class variables in Java, 58-60 , 59, 171 , 172 , 174 , 195 , 198 , 590
classes in Java, 4 , 26 , 26, 30 , 36-43 , 77 , 90-92 , 170 , 195 , 680-681
abstract classes in, 343-345 , 348 , 371
Abstract Windowing Toolkit (AWT) and, 440
allocating, 36-37
angle brackets in, 33
anonymous inner classes in, 588-590
arrays and, 215-217
base class in, 322
block of, 33
braces in, 33-34
browsers, class browsers for, 327 , 327
calling methods from parent classes in, super keyword, 325 , 346
calling, 70
casting objects in, 363-365
child classes in, 322-323 , 331 , 374
class definition for, 90
class instance creation expressions in, 91
class methods in, 61
class variables in, 58-60 , 59
constructors for, 66-69 , 78 , 336 , 345-346
containment in, 369 , 370
copying objects for, 358-360
copying, in OS/390 and OS/400, 700-701 , 732
data types in, 38
declaring, 33 , 36 , 90
deep inheritance in, 326-327 , 326
defining, 31-32 , 31, 33 , 36 , 90
design patterns for, 328-334 , 372 , 376
dot operator to call methods in, 40 , 77
dynamic loading of, 47
encapsulation in, 317-319 , 317, 317
enforcing inheritance in, 337-342
enumerating objects in, 360-363
extending, 319 , 321-323
factory method patterns in, 328-334 , 372 , 376
final modifier on methods in, 336-337
garbage collector for, 38
helper classes, AS/400 Toolbox and, 739-740
hierarchical file system (HFS) to store, 27
inheritance in, 334-347
inner classes in, 586-588
instantiation of, 36-38 , 77
integrated file system (IFS) to store, 27
interface variables in, 198
interfaces in, 347-363 , 371
is a relationships in, 369-370 , 373
JDK and, searching for, 383-385 , 383, 384
members in, 40-43
meta classes, 681
methods in, 39 , 40 , 90
modifiers for, 33
multiple inheritance in (See interfaces in)
naming, 33 , 37
object factories for, 328-334 , 372 , 376
object reference variables in, 37-38, 38, 40-42
objects as, 36 , 38
OS/400 and Java, 741-743
overriding methods in, 323-325 , 336 , 365-366
package modifier in, 33 , 52 , 335-336
packages for, 602-603
parent classes, 320-321
polymorphism in, 327-334 , 338-342
private modifier in, 335-336
protected modifier in, 335-336
public modifier in, 335-336
qualifying methods and variables in, 41-42
references to, 37
restricting access to, 43 , 335-336
singletons, 420
subclass in, 322
superclass in, 322 , 376
syntax of, 32-34, 32, 37
usage in, 369
use of, 36-39
variables in, 31 , 36-37 , 39 , 40 , 90
wrapper classes in, 192-195 , 166
classes in object-oriented COBOL, 679 , 682-686 , 688-691
CLASSPATH environment variable in Java, 54-56 , 77
OS/390 OMVS shell and, 701-703
OS/400 and Java, 733-735
clear method in Java hashtable, 231
client/server applications in Java, 4
Remote Method Invocation (RMI) and, 593-597
clone method, 358-360 , 366
clone method in Java vectors, 222
Cloneable interface, 358-360 , 376
CLOSE, 520
CLOSE SQL, 525 , 554
COBOL
access rights to names in, 64
activation groups in, 674
arithmetic expressions in, 90 , 100-101
assignment operators in, 101-103 , 119
automatic data items in, 30
batch updates in, 22
binary operators in, 90 , 99 , 100-101
binding directories in, 676
binding in, 26 , 26, 674-675
calling in, 27 , 44-45 , 46, 69
casting objects in, 363-365
CODE/400 for, 5 , 6 , 81
command-line parameters in, 72 , 78
comments in, 80
common gateway interface (CGI) and, 626
compilation units in, 670-671
compiler-directing statements in, 90
compiling, 26-27 , 26, 674-675 , 674, 669-670 , 670
computational expressions in, 98 , 99-115
conditional statements in (See also decision structures), 90 , 122
control structures in, 121
copybooks in, 8
data items in, 30 , 32
database access in, 22 , 515-532
decision structures in, 122 , 124-133
delimited scope statements in, 90
divisions in, 89
dynamic link libraries (DLL) in, 49-50 , 49, 64 , 77
ELSE IF in, 127 , 159
ELSE in, 127 , 159
encapsulation in, 318-319
EVALUATE in, 128-132
executable load modules in, 28
exporting in, 64 , 78
expressions in, 29 , 90 , 98-115
fetch expressions in, 98-99 , 115-116
file sorting in, 22
flow control in, 28-30 , 657-665 , 664, 667-677 , 675
functions in, 28
global data items in, 30
hello world program in, 73
IF in, 124-129 , 159
imperative statements in, 90
intrinsic functions in, 28 , 29
Java Native Interface (JNI) and, 591-593 , 615-617
Java vs., 1 , 21-22 , 32 , 71-72 , 72, 89-92 , 246-247
keywords in, 88
language access through, 22
linkage editing (See binding)
local data items in, 30
logical operators in, 106-109
loops in, 122 , 133-149
modules in, 26 , 28 , 28
nested ifs in, 127
nested programs in, 29 , 77
object-oriented (OO), 45
operators in, 90 , 98-115
OS/390 support for, 6
OS/400 support for, 6
paragraphs in, 29 , 32 , 77 , 89
parameter passing in, 48
pass-by-reference in, 48
pass-by-value in, 48
performance of, 22
precedence of operators in, 90 , 114-115
programs in, 32 , 39 , 44 , 89-90
qualified names in, 62-63
relational operators in, 106-109
reusing code in, 49-50
scoping in, 62-63
sections in, 29 , 89
sentences in, 89
sequence structures in, 121
service programs in, 49-50 , 64 , 77 , 673-674 , 676-677
statements in, 89 , 90
static data items in, 57
string manipulation in, 242 , 244-247
strongly typed variables in, 162
subprograms in, 71
threads in, 395
transfer structures in, 122 , 150-159
unary operators in, 90 , 99 , 105 , 106
user interface in, 437
variables in, 88-89
VisualAge for, 5 , 6 , 81
writing to command line in, 73-74 , 73
CODE Designer Tool for COBOL user interface, 437
CODE/400, 5 , 6 , 81 , 763
OS/400 and Java, 735 , 737 , 741
collection, COBOL database access and, 517
collections in Java, 21 , 199
color in Java user interface and, 443-444 , 446 , 506
columns, in COBOL database access and, 517
command beans, 642
CommandCall class, 738 , 742 , 743
command-line parameters in COBOL, 72 , 78
command-line parameters in Java, 72-73 , 78
comments in COBOL, 80
comments in Java, 40 , 80-88 , 119
COMMIT, 520-521 , 520
commitment control
COBOL database access and, 520-521
Java/SQL database access, 562-563
COMMON attribute COBOL, 29
Common Connector Framework (CCF), 705 , 706-708 ,707, 760
common gateway interface (CGI) (See also applets; Servlets), 15-16 , 626
Common Object Request Broker Architecture (CORBA), 642
object-oriented COBOL and, 683
OS/390 and Java, 703 , 722-725
OS/400 and Java, 737
packages for, 603
thread support in, 417
VisualAge for Java support, 655
communications support in Java, 21
COMP/COMP-1/COMP-2 COBOL data types, 166
companion CD-ROM, software loading instructions , 761-763
Comparable interface, 219-220 , 355
compare, 47
compareTo, 276
comparing strings in COBOL, 244
comparing strings in Java, 243-244
compilation units in COBOL, 670-671
compile time vs. runtime, 333-334
compiler-directing statements in COBOL, 90
compile-time arrays in COBOL, 203-204
compile-time arrays in Java, 203-204 , 239
compiling COBOL, 26-27 , 26, 674-675 , 674, 669-670 , 670
compiling Java, 4, 7-8, 7, 8, 22, 26-28, 77
OS/390 static compiler for, 28
OS/400 direct execution compiler for, 28
computational expressions in COBOL, 98 , 99-115
computational expressions in Java, 92 , 98 , 99-115 , 119
COMPUTE, 37 , 44 , 90 , 93 , 100 , 101 , 102 , 104 , 106 , 188-192 , 283-284
concat, 243-246 , 248-249
concatenating COBOL strings, 247-249 , 247
concatenating Java strings
using append method, 268
with concat method, 243-244 , 248-249
with plus sign operation, 242 , 243-245 , 249
conditional statements in COBOL (See also decision structures), 90 , 122
conditional statements in Java (See also decision structures), 113-114 , 122 , 132-133
Connection interface, 537
connectors, OS/390 and Java, 705
constraints in Java/SQL database access, 568 , 569
constructors in Java, 66-69 , 78 , 336 , 345-346
container-managed persistence in Enterprise Java
Beans (EJBs), 643
containers
for Enterprise Java Beans (EJBs), 643
Java user interface and, 440 , 445
containment in Java classes, 369 , 370
contains method, 222 , 231
containsKey method, 231
containsValue method, 231
context menus in Java user interface, 469
CONTINUE, 96 , 122 , 153
continue statement in Java, 96-97 , 150-155 , 160
contraction of operators in Java, 102-103 , 120
control structures in COBOL, 121
control structures in Java, 121
conversion classes for OS/400 and Java, 746 , 747
conversion specifiers in COBOL date/time, 285-287
CONVERT-DATE-TIME, 289
COPY, 31
Copy to Stream File (CPYTOSTMF), 732
copybooks, Java lack of, 8
copying objects between Java classes, 358-360
copyInto method in Java vectors, 222
copyValueOf, 276
CORBA (See Common Object Request Broker Architecture)
cos, 118
country-specific formatting for Java user interface , 499-504
Create Binding Directory (CRTBNDDIR) in, 676
Create Bound COBOL (CRTBNDCBL), 670
Create COBOL Module (CRTCBLMOD), 670
Create Program (CRTPGM), 670
CREATExxx, in Java/SQL database access, 550
creating Java code, 27 , 650 , 651
Cross System Coupling Facility (XCF), COBOL
database access and, 519
CURRENT DATE, 280 , 281 , 282 , 293
currentTimeMillis method, 290 , 291-293 , 294-295 , 314
cursors in SQL, 525 , 553-554
Customer Information Control System (CICS)
CICS BMS maps in COBOL user interface, 437
CICS classes and transactions in OS/390, 714-722
CICS Transaction gateway for COBOL database access, 518
COBOL database access and, 517 , 518
OS/390 and Java, 694 , 703 , 704 , 707 , 722-725
OS/400 and Java, 760


 Team-Fly

Index

D

daemon threads, 415-416
data areas, OS/400 and Java, 738
data definition language (DDL) for SQL, 530 , 531 , 530
Data Description Specifications (DDS)
COBOL database access and, 516-517
OS/400 and Java, 750-752
DATA DIVISION, 172 , 520 , 687
Data Division class in object-oriented COBOL, 685
data hiding in Java, 42-43 , 78
data in COBOL and database access, 517-519
data items in COBOL (See variables in Java), 30 , 32 , 161-195
casting, 188-192 , 195
categories of, 162-163
character data, 244-247
DATE FORMAT, 168
decimal places in, 183-187 , 195 , 196
declaring, 172
defining, 168 , 169-170
figurative constants in, 181
FILE SELECTION, 168
FORMAT, 168
GLOBAL, 172
groups of, 170
initialization of, 178 , 195
level-66 and level-88, 170
lifetime of, 174
LINKAGE SECTION, 168
literals in, 178-181
LOCAL STORAGE, 168 , 172
nested programs and, 172
numeric conversion for, 190-192
numeric data type in, 181-183
parameter passing in, 172-173
PICTURE, 168
ranges for numeric data type in, 181-183
scope of, 172 , 175
strings as, 244-247
strongly typed, 162
USAGE, 168
WORKING STORAGE, 168 , 172
data manipulation language (DML) for SQL, 530 , 531
data queues, OS/400 and Java, 738
data types in COBOL
BINARY, 166
BOOLEAN, 167
casting, 188-192 , 195
COMP/COMP-1/COMP-2, 166
DATE FORMAT, 167
DISPLAY, 166
DISPLAY-1, 166 , 167
edited, 166
figurative constants in, 181
FORMAT DATE, 167
FORMAT TIME, 167
FORMAT TIMESTAMP, 167
INDEX, 167
Java vs., 166-167
literals in, 178-181
memory requirements of, 182
numeric conversion for, 190-192
OBJECT REFERENCE, 167
POINTER, 167
PROCEDURE POINTER, 167
TYPEDEF, 167
data types in Java (See also string manipulation), 34 , 40 , 109 , 164-197
OS/400 and Java, 740
BigDecimal, 166 , 184-187 , 195 , 196
boolean, 165 , 167 , 181 , 187-188 , 195 , 197
byte, 164 , 165 , 197
casting, 188-192 , 195
char, 165 , 166 , 180 , 195 , 197
COBOL vs., 166
date and time, 167
Date class in, 167
decimal places in, 183-187 , 195 , 196
DecimalFormat class in, 166
double, 165 , 166 , 191 , 195 , 197
escape sequences for, 180
float, 165 , 166 , 180 , 183-187 , 191 , 195-197
int, 164 , 165 , 166 , 192 , 195 , 197
literals in, 178-181
long, 164 , 165 , 166 , 192 , 195 , 197
memory requirements of, 182
numeric, 165 , 180 , 181-183 , 195-197
numeric conversion for, 190-192
object reference, 167
primitive, 164 , 195 , 197
ranges for numeric, 181-183
reference type, 164 , 195 , 197
short, 164 , 165 , 166 , 195 , 197
SQL data types vs., 561-562
String (See also char), 165 , 166 , 167 , 195 , 241
Time class in, 167
true/false (See boolean), 165
Unicode standards for char data type, 165 , 195
wrapper classes in, 166 , 192-195
database access in COBOL (See also SQL for COBOL database access), 22 , 515-532
ACCESS MODE in, 520
Application Programming Interfaces (APIs) and , 516-517
basic sequential access method (BSAM) in, 516
call level interfaces (CLIs) and, 516
CICS Transaction gateway, 518
CLOSE in, 520
collection in, 517
columns in, 517
COMMIT in, 520-521
commitment control in, 520-521
Cross System Coupling Facility (XCF) in, 519
Customer Information Control System (CICS) in, 517 , 518
Data Description Specifications (DDS) for, 516-517
DATA DIVISION in, 520
data in COBOL and, 517-519
DB2 for OS/390 (DB2UDB Server), 516
DB2 Universal Database for AS/400 , 516
DB2/400 for, 516
DELETE in, 519 , 520
embedded SQL in, 516-517
EXTEND in, 520
fields in, 517
file description (FD) sentence in, 520
files in, 517
IMS TCP/IP OTMA in, 519
indexes in, 517
Information Management System (IMS) DB for , 516
INPUT in, 520
I-O in, 520
Java Customer Information Control System (JCICS) in, 518
Java Database Connectivity (JDBC) in, 518
logical files in, 517
MQSeries client for Java in, 519
Open Transaction Manager Access (OTMA) in , 519
ORGANIZATION IS in, 520
OS/390 data in, 516
OS/400 data in, 516-517
OUTPUT in, 520
queued sequential access method (QSAM) in, 516-517
READ in, 519 , 520
record formats in, 517
RECORD KEY in, 520
record-level access in, 516-521
records in, 517
REWRITE in, 520
ROLL, ROLS, ROLU in, 520
ROLLBACK in, 520-521
rows in, 517
schema in, 517
SELECT in, 519
SETU/SETS in, 520
SQL and (See also), 516-518 , 522-532
SQLJ in, 518
START, 520
Structure Query Language (See SQL)
tables in, 517
terminology of, 517
third-party tools for, 516
transaction monitoring in, 517
views in, 517
virtual sequential access method (VSAM) in, 516 , 517 , 522
WRITE in, 519 , 520
database access in Java, 65 , 532-582
arrays in, 572
batch updates in, 574-575
binary large objects (BLOB) in, 572
CallableStatement for, 537 , 556 , 558-559
character large objects (CLOB) in, 572
closing database connections in, 539 , 559
closing statements in, 539 , 558-559
commitment control in, 562-563
connecting to database for, 538 , 541-547
Connection interface for, 537
constraints in, 568 , 569
CREATExxx, 550
creating SQL statement objects for, 538 , 547-548
cursors in SQL and, 553-554
data types in, SQL vs. Java, 561-562
database driver managers in, 533
database drivers in, 533 , 534
DatabaseMetaData in, 537 , 567-568
DB2 Universal Database and, 535
DB2/390 and JDBC drivers for, 534-535 , 575
DB2/400 and JDBC drivers for, 535-536 , 575
DELETE in, 550
DISTINCT in, 572
driver connection property values for, 544-546
Driver interface for, 537
DriverManager.getConnection and, 541-547
embedded SQL (See SQLJ)
Enterprise Java Beans (EJBs) and, 575
error handling in, 538 , 557-558
execute method in, 550
executeQuery in, 549 , 556
executeUpdate, 550
importing java.sql package for, 537 , 539
INSERT in, 550
interfaces in JDBC in, 536-537
Internet Inter Orb Protocol (IIOP) and, 534
Java Database Connectivity (JDBC) in, 533 , 533 , 537-560 , 572
Java Naming and Directory Services (JNDS) for , 575
Java Native Interface (JNI) for, 534
Java Transaction Services (JTS) for, 575
java.sql package for, 533 , 536-539
JAVA_OBJECT in, 572
JDBC/ODBC bridge in, 533 , 534
keyed files, OS/400 and Java, 756-758
loading JDBC drivers for, 537 , 539-541
metadata in, 563-568
named iterators in SQLJ, 578
net drivers for, 534
null value considerations for, 562
in OS/400 , 738
positioned iterators in SQLJ, 578
positioned update or delete in SQL, 554
PreparedStatement in, 537 , 547-548 , 556 , 558-559
queries in, 556 , 559-560
reading rows in (readNextRows), 554-557
REF in, 572
referential integrity (RI) constraints in, 568 , 569
remote method invocation (RMI), 534
result sets in, 550-554 , 556 , 573-574
ResultSet in, 537 , 573-574
ResultSetMetaData interface for, 537 , 566-567
retrieving SQL information for, 538 , 550-554
running SQL statement objects for, 538 , 548-550
schema in, 544
searched update or delete in, 554
SELECT in, 549 , 556
set methods for, 548-550
SQLJ for, 576-582
Statement in, 537 , 547-548 , 556 , 558-559
stored procedures in, 568 , 569-572
STRUCT in, 572
thin drivers for, 534
threading and, 555-556
transaction isolation in, 562-563
triggers in, 568 , 569
try/catch blocks in, 538
UPDATE in, 550
VisualAge for Java and, 655
WebSphere and, 534 , 575
database driver managers, Java/SQL database access , 533
database drivers, Java/SQL database access, 533 , 534
DatabaseMetaData, Java/SQL database access, 537 , 567-568
DataQueue class, OS/400 and Java, 747-750
DATE, 280 , 285 , 287
date and time in COBOL, 279-289 , 658 , 668
ACCEPT in, 282 , 293
ACCEPT DATE in, 279-280
ADD-DURATION in, 289 , 298
arithmetic manipulation of, 284-285 , 298
century window for, 282 , 283 , 288
comparing, 296-297
COMPUTE in, 283-284
conversion specifiers in, 285-287
CONVERT-DATE-TIME in, 289
converting formats of, 282
CURRENT DATE in, 280 , 281 , 282 , 293
DATE in, 280 , 285 , 287 , 288
DATE FORMAT in, 283 , 284
DATE-OF-INTEGER in, 281 , 282
DATEPROC in, 283 , 284
DATE-TO-YYYMMDD in, 281 , 282 , 283
DATEVAL in, 281 , 282
DAY in, 280
DAY-OF-INTEGER in, 281 , 282
DAY-OF-WEEK in, 280
DAY-TO-YYYYDDD in, 281 , 282 , 283
DISPLAY in, 285
durations and, 289 , 298
EXTRACT-DATE-TIME in, 289
extracting parts of, 300-301
FIND-DURATION in, 289 , 298
FORMAT in, 285 , 287 , 288
format literals in, 285 , 287
formatting of, 280-288 , 303
Gregorian calendar vs. others in, 280 , 281
ILE COBOL on OS/400 and, 285-289
INTEGER-OF-DATE in, 281 , 282 , 301
INTEGER-OF-DAY in, 282 , 301
Julian calendar vs. others in, 280 , 281
MOVE in, 283-284
PIC coding of, 280 , 285
rolling over dates in, 284-285 , 298
SUBTRACT-DURATION in, 289 , 298
TEST-DATE-TIME in, 289
TIME in, 280 , 285 , 287 , 288
TIMESTAMP in, 285 , 287
two- vs. four-digit years in, 280 , 282-284 , 288 , 296-297 , 314
UNDATE in, 282 , 284
VisualAge Millennium Language Extensions for , 283
YEAR-TO-YYYY in, 282 , 283
YEARWINDOW in, 282 , 283
YEAR-YYYY in, 282
date and time in Java, 21 , 167 , 289-314 , 289
add method, 299-300
after method for, 297
arithmetic manipulation of, 297-300
before method for, 297
Calendar class in, 295-296
comparing, 296-297
computer's CPU timekeeping in, 290-293
country-specific formatting for, 309
current date/time, retrieving, 293-296
currentTimeMillis method in, 290 , 291-295 , 314
Date class in, 290 , 294 , 296 , 298 , 314
DateFormat class in, 308
default formatter for, 308
durations in, 294 , 298-300
elapsed time computation in, 291-293
epoch of computer-based time in, 291 , 314
equals method for, 297
extracting parts of, 300-303
formatting, 303-311 , 314
get method for extracting, 302-303
getDateInstance in, 308 , 314
getDateTimeInstance in, 308 , 314
getTime method in, 294-295
getTimeInstance in, 308 , 314
GregorianCalendar class in, 290 , 294-296 , 298-301 , 311-312 , 314
leap years in, 311-312 , 314
as objects in, 297
rolling over dates in, 297-300
short, medium, long, full formatting for, 309-311
SimpleDateFormat class in, 290 , 303-306 , 308 , 314
storing dates, 296
Time class in, 296
time formatting, 306-308
Timestamp class in, 296
TimeZone class in, 290 , 312-313 , 314
zero-based nature of, 295
Date class, 167 , 290 , 294 , 296 , 298 , 314
date data item in COBOL, 163
DATE FORMAT, 167 , 168 , 284
DateFormat class, 308
DATE-OF-INTEGER, 281 , 282
DATEPROC, 284
DATE-TO-YYYMMDD, 281 , 282 , 283
DATEVAL, 281 , 282
DAY, 280
DAY-OF-INTEGER, 281 , 282
DAY-OF-WEEK, 280
DAY-TO-YYYYDDD, 281 , 282 , 283
DB2 for OS/390 (DB2 UDB Server), 516 , 575
DB2 Universal Database for AS/400 , 516 , 535 , 575
DBCS data item in COBOL, 162
DDS display files in COBOL user interface, 437
deadlock in threading, 429-430 , 429
deallocating objects in object-oriented COBOL, 690-691
debugging Java code with VisualAge, 651-652 , 652
decimal places in Java, 183-187 , 195 , 196
DecimalFormat class in Java, 166
decision structures in COBOL, 122 , 124-133
AT END, 122
ELSE/ELSE IF, 127 , 159
EVALUATE, 122 , 128-132 , 160
IF, 124-129 , 159
INVALID KEY, 122
nested ifs in, 127
ON EXCEPTION, 122
ON OVERFLOW, 122
ON SIZE ERROR, 122
SEARCH...WHEN, 122
decision structures in Java, 91 , 122 , 124-133
case statement in, 131-132
conditional (ternary) operator in, 132-133
else/else if in, 127 , 159
if, 122 , 124-129 , 132-133 , 159
method call, 122
nested ifs in, 127
switch, 122 , 128-132 , 159
system.exit, 160
decrement
pre/post increment/decrement expressions in Java for, 103-106
SUBTRACT or COMPUTE in COBOL for, 106
deep inheritance in, 326-327 , 326
definition-side deck to DDL in COBOL, 661
DELETE, 519 , 520
DELETE (SQL), 523 , 524 , 550
delimited scope statements in COBOL, 90
DeMorgen's rule, 109
deployment descriptors in Enterprise Java Beans (EJBs), 644
design patterns, 328-334 , 372 , 376
destroy, 628
determinant loops (See for loop in Java; PERFORM VARYING)
Dialog in Java user interface, 440 , 445 , 448-448 , 449
digital certificates
packages for, 603
in OS/400 and Java, 739
dimensions in Java arrays, 200
directives for Java variables, 635
directory creation in Java, 612-613
directory listing in Java, 606-611
DISPLAY, 73 , 285
DISPLAY COBOL data type, 166
DISPLAY-1 COBOL data type, 166 , 167
DISTINCT, 572
distributed objects and RMI, 593
Distributed Relational Database Architecture (DRDA) SQL, 530
distributed SQL, 529-530
Distributed Unit of Work (DOW) SQL, 530
DIVIDE, 101
DIVISION, 31
division (See operators in COBOL; operators in Java )
divisions in COBOL, 89
document object model (DOM), 19 , 645
document type definition (DTD) in XML, 645-646
documentation, forced documentation of code using throws clause, 392
doGet, 628
domain names, in URLs, 13-14
Domino Go Webserver and OS/390 , 694
doPost, 628 , 630
dot operator for Java variables, 175
dot operator to call methods in Java, 40 , 77
double byte character set (DBCS) in COBOL, 668
Double class in Java, 192-194
double data type in Java, 165 , 166 , 191 , 195 , 197
do-until loop, 133 , 147-149 , 160
do-while, 122 , 133 , 147-149 , 160
drag-and-drop support for Java user interface, 505
drive listing in Java, 611-612
Driver interface for, 537
Dropdown in Java user interface, 506
durations in COBOL date/time, 289 , 298
durations in Java date/time, 294 , 298-300
dynamic SQL, 524
dynamic calls in COBOL, 659 , 663 , 671 , 675
dynamic HTML (DHTML), 19 , 22
dynamic link libraries (DLL), 49-50 , 49, 64 , 77 , 658 , 660-662 , 662-664
distribution of, 664-665
Java Native Interface (JNI) and, 591-593 , 615-617
dynamic loading of Java classes, 47
dynamic SQL, 527


 Team-Fly

Index

E

edited COBOL data type, 166
editing Java code with VisualAge, 650-651
elapsed time computation in Java, 291-293
elementAt method in Java vectors, 222
elements in Java arrays, 199 , 206
elements method, 222 , 232
ELSE, 127
ELSE IF, 127
embedded SQL, 524-529
COBOL database access and, 516 , 517
SQLJ for, 576-582
empty block in Java, 96
empty parameters in Java methods, 45
empty statements in Java, 91 , 95-96
encapsulation, 64 , 66 , 316 , 317-319 , 317, 375
Encina support for OS/400 and Java, 760
END PERFORM, 95
END PROGRAM, 29
END-ADD, 90
END-IF, 126
endsWith, 276
ensureCapacity method in Java vector, 223
Enterprise Access Builder (EAB)
OS/390 and Java, 705 , 708-712 , 710
OS/400 and Java, 760
enterprise Java beans (EJBs), 20-22 , 23 , 373 , 642-644
bean-managed persistence (BMP) in, 643
container-managed persistence in, 643
containers for, 643
deployment descriptors in, 644
entity beans as, 643
home interface for, 643-644
interfaces for, 643-644
Java Naming and Directory Services (JNDS) for, 643
Java/SQL database access, 575
OS/390 and Java, 695
OS/400 and Java, 728
persistence in, 643
remote interfaces for, 643-644
session beans as, 643
VisualAge for Java, 655
Enterprise Toolkit in VisualAge for Java, 655
entity beans as Enterprise Java Beans (EJBs), 643
entry sequenced data sets (ESDS) in OS/390 and Java , 713
Enumeration interface, 360-363 , 376
Environment Division in object-oriented COBOL , 684-685 , 687
epoch of computer-based time, 291 , 314
equals method, 243-244 , 253 , 276 , 277 , 297 , 366
equals method for Java arrays, 218
equalsIgnoreCase, 276 , 277
error checking
COBOL, 658
Java user interface and, 490-492 , 490
in SQL, 526 , 557-558
Error class, 393
ERROR declaratives for COBOL exception handling , 378
error messages in Java user interface, 492-494
Error subclass, 381 , 381
escape sequences for Java char data, 180 , 245
EVALUATE, 90 , 122 , 128-132 , 160
event model for Java user interface, 473-474
events
Java beans, 622
Java user interface and, 440 , 473-481 , 506
Exception subclass, 381 , 381, 393
exceptions in COBOL
APIs for, 378
AT END in, 379
AT INVALIDKEY in, 379
CEEHDLR API for, 378
CEEHDLU API for, 378
clauses on language statements for, 378
condition or exception model in, 377-379 , 393
ERROR declaratives for, 378
FILE STATUS in, 379
file status data items for, 378
function check in, 378
ON EXCEPTION in, 378
ON SIZE ERROR in, 378
OS/390 and OS/400 exception model in, 377-379 , 393
termination in, 378
USE AFTER ERROR in, 379
exceptions in Java, 379-393
OS/400 and Java, 740
catch for, 388-391
creating exception classes/subclasses, 385-386 , 393
Error subclass for, 381 , 381, 393
Exception subclass for, 381 , 381, 393
finally block for, 390-391 , 393
forced documentation of code using throws clause , 392
method signatures and, 391 , 393
monitoring exceptions with try/catch blocks, 388-391
Runtime Exception class for, 381-382
searching documentation for classes in, 383-385 , 383, 384
sending exceptions with throw statement, 386-388 , 391-392
severity of, 380-381
throw statement for, 386-388 , 391-392 , 393
Throwable class and, 379-381 , 381
try/catch blocks for, 388-391 , 393
exclamation sign operator, 107-109
exclusive OR, 107-109
EXEC SQL/END-EXEC SQL, 524
executable load modules in COBOL, 28
execute method, Java/SQL database access, 550
executeQuery, Java/SQL database access, 549 , 556
executeUpdate, Java/SQL database access, 550
EXIT, 152
exit in Java, 159
EXIT PROGRAM, 122 , 156
exp, 118
exponent (See operators in COBOL; operators in Java)
EXPORTALL, 64
exporting in COBOL, 64 , 78
expressions in COBOL (See also operators in
COBOL), 29 , 90 , 98-115
computational expressions in, 98 , 99-115
fetch expressions in, 98-99 , 115-116
expressions in Java (See also operators in Java), 92 ,
98-115
array index expression in, 116
assignment expressions in, 91 , 101-103
computational expressions in, 92 , 98 , 99-115 , 119
expression statements in, 91 , 92
fetch expressions in, 92 , 98-99 , 115-116 , 119
method call expressions in, 116
null expressions in, 116
operators in, 92-93
variables assigned with, 93
EXTEND, 520
extending Java classes, 319 , 321-323 , 375
extends Thread method, 401-405 , 412
Extensible Markup Language (XML) and, 644-647
EXTERNAL clause, 30 , 64 , 174 , 175 , 178
EXTERNAL data items in COBOL, 668
external floating-point data item in COBOL, 163
EXTRACT-DATE-TIME, 289


 Team-Fly

Index

F

factory method patterns, 328-334 , 372 , 376
fetch expressions in COBOL, 98-99 , 115-116
fetch expressions in Java, 92 , 98-99 , 115-116 , 119
field description classes in OS/400 , 750-751
fields, in COBOL database, 517
figurative constants in COBOL, 181
file creation in Java, 613-614
file description (FD) sentence in COBOL database
access and, 520
file listing in Java, 606-611
FILE SELECTION, 168
file sorting in COBOL, 22
FILE STATUS, 379
file system and Java packages, 53-54 , 54
files, in COBOL database, 517
fill method for Java arrays, 218
filter criteria in SQL, 523
final methods and classes in Java, 336-337
finalize method, 366
finally block for Java exceptions, 390-391 , 393
FIND-DURATION, 289 , 298
firewalls, 14
firstElement method in Java vector, 223
filter criteria in SQL, 522
Float class in Java, 192-194
float data type in Java, 165 , 166 , 180 , 183-187 , 191 , 195-197
floor, 118
flow of control in COBOL, 28-30
flow of control in ILE COBOL, 667-677 , 675
activation groups in, 674
Add Binding Directory Entry (ADDBNDDIRE) in, 676
Application Development Manager (ADM) for, 677
applications using, 670-671 , 670
binding directories in, 676
binding in, 674-675
CALL LINKAGE TYPE, 672
CALL, 671 , 675
COBOL/400 applications in, 669-670 , 669
compilation units in, 670-671
compiling 674-675 , 674
Create Binding Directory (CRTBNDDIR) in, 676
Create Bound COBOL (CRTBNDCBL), 670
Create COBOL Module (CRTCBLMOD), 670
Create Program (CRTPGM), 670
date and time in, 668
distributing service programs in, 676-677
double byte character set (DBCS) in, 668
dynamic calls in, 671 , 675
enhancements to, 668
EXTERNAL data items in, 668
intrinsic functions in, 668
locales, 668
modules in, 671
nested COPY in, 668
nested programs in, 668
null-value support, 668
parameter passing in, 668
pass by reference, 668
pass by value, 668
PERFORM, 675
PROCEDURE DIVISION, 672 , 675
procedure pointers in, 668
procedures, ILE, 672 , 675
program objects in, 671
running programs in, 675
service programs in, 673-674 , 676-677
static calls in, 671 , 675
two- vs. four-digit years in, 668
TYPEDEF, 668
user-defined data types, 668
flow of control in Java, 25-78
access control in, 65
access rights to names in, 63-66
accessors (getters) in, 66
calling methods in, 45-46 , 46
calling programs in, 69-71
class methods in, 61
classes in, 26 , 26, 30 , 36-43 , 77
CLASSPATH environment variable in, 54-56 , 77
COBOL vs., 71-72 , 72
command-line parameters in, 72-73 , 78
comments in, 40
compiling and, 26-28 , 77
constructors in, 66-69 , 78 , 336 , 345-346
creating source code in, 27
data hiding in, 42-43 , 78
data types in, 40
defining classes in, 31-32 , 31
dot operator to call methods in, 40 , 77
encapsulation in, 64 , 66
functions in (See methods)
garbage collector for, 38
getters (accessors) in, 66
hello world program in, 74-76
instance variables in, 41 , 66 , 77
javac compiler for, 27-28 , 27, 77
local variables in, 31
main method in, 44 , 69-71 , 77
methods in, 31 , 34-35 , 39 , 40 , 45-49 , 77
modifiers in, 65 , 78
mutators (setters) in, 66
object reference variable in, 77
overloading methods in, 46-47 , 78 , 218 , 262
package access in, 64-65
packages in, 50-57 , 50, 70
parameter passing in, 72-76
prototyping methods in, 47-48
qualified names in, 62-63
qualifying methods and variables in, 41-42
reusing code in, 49-57
running Java programs in, 43 , 44
scoping in, 62-63
semicolon to end statements in, 34
setters (mutators) in, 66
signature of methods in, 70
standard output in, 74-76
static data items in, 57-62
static methods in, 60-62
static variables in, 58-60 , 59
syntax of, 32-35
this predefined object variable in, 42
variables in, 34 , 40 , 77
VisualAge for, 27
writing to command line in, 74-76
flow of control in OS/390 COBOL, 657-665 , 664
ANSI COBOL standards and, 658
application structure using, 658-660 , 659, 660
CALL in, dynamic vs. static, 659 , 659, 663
date and time in, 658
definition-side deck to DDL in, 661
distributing DLLs in, 664-665
dynamic link libraries (DLL) in, 658 , 660-664 , 662, 663
enhancements to, 658
error handling in, 658
interoperability improvements in, 658
Language Environment (LE) services in, 658
LINKAGE SECTION, 664
migration path to, 657-658
nested programs in, 660 , 660
object-oriented (OO) COBOL and, 658
pre-link step to DDL in, 661
PROCEDURE DIVISION, 660 , 663
recursive calls in, 658
WORKING STORAGE, 664
FlowLayout manager in Java user interface, 450 , 453-454 , 454
focus of objects in Java user interface, 443
font selection in Java user interface, 443 , 444 , 446
for loop in Java, 97-98 , 122 , 133 , 134-143 , 160
forced documentation of code using throws clause , 392
foreground color in Java user interface, 443 , 446
FORMAT, 168 , 285 , 287
FORMAT DATE, 167
format literals in COBOL date/time, 285 , 287
FORMAT TIME, 167
FORMAT TIMESTAMP, 167
forms using HTML, 15
Frame in Java user interface and, 440 , 445 , 446 , 451-452
frameworks in object-oriented programming, 372 , 376
freeing or deallocating objects in object-oriented
COBOL (See garbage collection in Java), 690-691
Freely Distributable Math Library (FDLIBM) in Java , 117
FROM clause SQL, 522-523
FTP in OS/400 and Java, 733
function check in COBOL exception handling, 378
FUNCTION keyword COBOL, 29
functions in COBOL, 28
functions in Java (See methods)


 Team-Fly

Index

G

garbage collector in Java, 9 , 38
gateways for OS/390 and Java, 704 , 705
get method
date/time extraction using, 302-303
input processing in Java user interface, 482-484
Java beans and, 622-623
get method in Java hashtable, 232
getBytes, 276
getChars, 276
getCheckboxGroup, 482
getDateInstance, 308 , 314
getDateTimeInstance, 308 , 314
getItem, 483
getLabel, 482 , 483
getSelectedCheckbox, 482
getSelectedIndex, 482 , 483
getSelectedItem, 482 , 483
getSelectedText, 483
getServletInfo, 628
getState, 482
getters (accessors) in Java, 66
getText, 482 , 483
getTime, 294-295
getTimeInstance, 308 , 314
GIVING, 44
GLOBAL, 30 , 64 , 172
global data items in COBOL, 30
GO TO, 122 , 152 , 156 , 160
GOBACK, 122 , 156
graphical user interface (GUI) (See user interface in COBOL; user interface in Java)
graphics in Java user interface, 499 , 505
GREATER THAN, 100
green screen in COBOL user interface, 437 , 439
Gregorian calendar vs. others in COBOL date/time , 280 , 281
GregorianCalendar class, 290 , 296 , 298-301 , 311-312 , 314
GridBagLayout manager for Java user interface, 450 , 456-461 , 459, 460
GridLayout manager for Java user interface and, 450 , 454-456 , 456
GROUP BY clause SQL, 523
group IDs for OS/390 and Java, 698
groups of data items in COBOL, 170


 Team-Fly

Index

H

has a relationships in object-oriented programming , 370 , 375
hashCode, 276 , 366
hashtables in Java, 21, 199, 229-233, 230, 239
clear method, 231
contains method, 231
containsKey method, 231
containsValue method, 231
elements method, 232
get method, 232
isEmpty method, 232
keys method, 232
put method, 232
remove method, 232
size method, 232
HAVING clause SQL, 523
hello world program in COBOL, 73
hello world program in Java, 74-76
helper classes, AS/400 Toolbox for OS/400 and Java , 739-740
helper methods for Java arrays, 217-222
hierarchical file system (HFS)
OS/390 and Java, 697-699 , 697
storing Java classes in, 27
High Performance Java (HPJ)
in OS/390 and Java, 694 , 714 , 725
in VisualAge for Java, 655
home interface for Enterprise Java Beans (EJBs), 643-644
Host On Demand support for OS/400 and Java, 760
HTTP server
configuration, using JSPs, 637-638 , 637
OS/390 and Java, 694
OS/400 and Java, 728
hyperlinks using HTML, 12 , 12, 13
hypertext markup language (HTML), 3 , 10-16 , 10, 11
applets (Java) and, 16-17
cascading style sheets for, 19
case-insensitivity of tags in, 11
Common Gateway Interface (CGI) and, 15-16
dynamic HTML (DHTML) and, 19 , 22
Extensible Markup Language (XML) and, 644-647
firewalls and, 14
formatting text with, 10
forms in, 15
forms, using Servlet to process information , 631-634 , 631, 633
hyperlinks using, 12 , 12, 13
hypertext transport protocol (HTTP) and, 13
interactive Web page creation using, 15-16
Internet interface using, 14-16
intranet use of, 14
Java and, 22
Java beans and, 20
Java Servlets and, 626-634
Java user interface and, 438 , 498-499 , 498
JavaDoc comments using, 82-88 , 87
JavaScript and, 18-20
JavaServer Pages (JSPs) in, 20 , 634-642
Program Call MetaLanguage (PCML) and, 757
static Web page creation using, 13 , 15
tags in, 10
uniform resource locators (URLs) and, 13
Web servers and, 13
hypertext transport protocol (HTTP) and HTML, 13


 Team-Fly

Index

I

Identification Division in object-oriented COBOL , 684 , 686-687 , 686
IDENTIFICATION DVISION, 80
IF in COBOL, 90 , 122 , 124-129 , 159
if in Java, 94-95 , 122 , 124-129 , 132-133 , 159
imperative statements in COBOL, 90
include files, Java lack of, 8
increment
using ADD or COMPUTE in COBOL, 104
using pre/post increment/decrement expressions in
Java, 103-106
indeterminant loops (See PERFORM UNTIL; while)
INDEX COBOL data type, 167
index data item in COBOL, 163
index name data item in COBOL, 163
indexes, COBOL and, 517
indexes, SQL, 530
indexOf, 246 , 256
indexOf method in Java vector, 223
Information Management System (IMS) DB
COBOL database access and, 516
gateways for OS/390 and Java, 703
MFS maps in COBOL user interface, 437
OS/390 and Java, 704 , 707
TCP/IP OTMA connection in OS/390 and Java, 694
TCP/IP OTMA connection in COBOL database
access and, 519
inheritance, 316 , 318-327 , 334-347 , 369 , 375 , 680
abstract classes in, 343-345 , 348 , 371
abstract methods in, 342-343
base class in, 322
calling methods from parent classes in, super keyword, 325 , 346
casting objects and, 364
child classes in, 322-323 , 331 , 374
constructor access and, 336 , 345-346
deep inheritance in, 326-327 , 326
enforcing, 337-342
extending classes and, 319 , 321-323
final modifier for Java classes and methods in , 336-337
interfaces and, 356
is a relationships in, 369-370 , 373
modifiers and, 335-336
multiple, 9 , 346-347
object-oriented COBOL and, 679
overriding methods in, 323-325 , 336 , 365-366
parent classes in, 320-321
preventing, 336
protecting access and, 335-336
subclass in, 322
superclass in, 322 , 376
init method, 628
INITIAL, 30 , 57 , 174
INITIALIZE, 102
inner classes in Java, 586-588
Inprise (See J/Builder)
INPUT, 520
Input Method Framework for Java user interface, 505
input processing
Java user interface and, 482-484
Servlets to process HTML input forms, 631-634 , 631, 633
insert method, 268-269 , 483
INSERT (SQL), 524 , 550
insertElementAt method in Java vector, 223
INSPECT, 246
INSPECT CONVERTING, 260-264
INSPECT REPLACING ALL, 259-260
INSPECT TALLYING, 246 , 254-258
instance of Java classes, 36-38 , 77
instance variables in Java, 41 , 58-62 , 59, 66 , 77 , 375
threads sharing of, 397-398 , 397
instantiation in object-oriented COBOL, 679 , 689-690
int data type in Java, 164 , 165 , 166 , 192 , 195 , 197
Integer class in Java, 192-194
INTEGER-OF-DATE, 281 , 282 , 301
INTEGER-OF-DAY, 282 , 301
Integrated Development Environment (IDE) and
VisualAge for Java, 648
integrated file system (IFS)
OS/400 and Java, 729-730 , 738
storing Java classes in, 27
interactive SQL, 524-526
interactive Web page creation using HTML, 15-16
interface definition language (IDL) in object-oriented COBOL, 683
interfaces in Java, 198 , 219 , 347-363 , 371 , 375 , 680
as abstract classes, 348 , 356 , 371
CallableStatement for, 537
as callbacks, 352-356
calling methods using, 350
Cloneable, 358-360 , 376
Comparable, 219 , 355
Connection, 537
constructors absent from, 348
DatabaseMetaData, 537
declaring, 348
defining, 348
Driver interface for, 537
Enterprise Java Beans (EJBs) and, 643-644
Enumeration, 360-363 , 376
inheritance in, 356
initialization of, 357
instantiation impossible with, 348
JDBC and, 536-537
KeyListener for, 491
methods in, 349 , 356
modifiers for, 356 , 357
naming, 349 , 356
polymorphism in, 349
PreparedStatement for, 537
ResultSet for, 537
ResultSetMetaData for, 537
Searchable, 355
searches using, 352-356
signatures, method signatures in, 347 , 348 , 350
Statement for, 537
uses for, 351-352 , 357
variables in, 349 , 356
WindowListener, 479-481
intern, 276
internal floating-point data item in COBOL, 163
internationalization in Java, 21
Internet addressing, 14
Internet Information Server (IIS), 16
Internet Inter Orb Protocol (IIOP), 642
Java/SQL database access, 534
OS/390 and Java, 722-725
OS/400 and Java, 737
Internet use of Java, 3 , 10
InterNIC and assignment of domain names, 14
interoperability improvements in COBOL, 658
interpreters for Java, 3 , 4 , 7-8 , 22
intranets, 14
intrinsic functions in COBOL, 28 , 29 , 668
introspection in Java beans, 622-623
INVALID KEY, 122
inverse operator (~) in Java, 112
INVOKE, 45
invoking methods in object-oriented COBOL, 688 , 690
I-O, 520
is a relationships in object-oriented programming , 369-370 , 373 , 375
isAlive method, 431
isDigit, 246 , 265-266
isEmpty method, 223 , 232
isEnabled, 443
IShell interpreter for OS/390 and Java, 697-699
ISPF panels in COBOL user interface, 437
isVisible, 443


 Team-Fly

Index

J

J/Builder, 5 , 737 , 741
JAR files
distribution of, 57
Java packages and, 55-56 , 77
Java, 1-23 , 1
access control in, 65
accessibility of, 4
AIX support for, 6
anonymous inner classes in, 588-590
applets in, 3 , 16-17 , 22 , 494-499
applications in, 18 , 22
arrays in, 21 , 198-220
assignment expressions in, 91 , 101-103
blocks in, 80 , 91 , 93-95
braces to delimit blocks in, 80 , 93 , 173
break in, 96-97
bytecode for, 4 , 7-8 , 7, 22
calling programs and commands in, 69-71 , 615-617
case statement in, 131-132
casting objects in, 363-365
class instance creation expressions in, 91
classes in, 4 , 26 , 26, 30 , 36-43 , 90-92 , 77 , 586-588
CLASSPATH environment variable in, 54-56 , 77
client/server roles for, 4
COBOL vs., 1 , 21-22 , 32 , 89-92 , 246-247
collections in, 21 , 199
command beans and, 642
comments in, 40 , 80-88 , 119
Common Gateway Interface (CGI) vs., 15-16
communications support in, 21
compiling of, 4, 7-8, 7, 8, 22, 26-28, 77
computational expressions in, 92 , 98 , 99-115 , 119
conditional statements in (See also decision structures), 122
constructors in, 66-69 , 78 , 336 , 345-346
continue in, 96-97
control structures in, 121
creating source code in, 27
data hiding in, 42-43 , 78
data types in, 34 , 40 , 109 , 164-197
date and time manipulation in, 21 , 289-314
decision statements in, 91 , 122 , 124-133
development of, 2-3
directives for variables in, 635
directory creation in, 612-613
directory listing in, 606-611
dot operator to call methods in, 40 , 77
double equals sign to compare variables in, 95 , 107-109
drive listing in, 611-612
else if, 127 , 159
else in, 127 , 159
empty statements in, 91 , 95-96
encapsulation in, 64 , 317-319 , 317
enterprise Java beans (EJBs) in, 20-22 , 23 , 373 , 642-644
equals sign to equate variables in, 37 , 95 , 101-103 , 119
expressions in, 91 , 92 , 98-115
Extensible Markup Language (XML) and, 644-647
fetch expressions in, 92 , 98-99 , 115-116 , 119
file creation in, 613-614
file listing in, 606-611
flow of control in (See Java flow control)
for in, 97-98 , 122 , 133 , 134-143 , 160
frameworks in, 372 , 376
functions in (See methods)
garbage collection in, 9 , 38
graphical user interface (GUI) (See user interface)
hashtables in, 21 , 199 , 229-333 , 230
hypertext markup language (HTML) and, 3 , 10-16 , 22
if in, 94-95 , 122 , 124-129 , 132-133 , 159
inheritance in, 319-327
inner classes of, 586-588
instance variables in, 41 , 66
interpreters for, 3
interfaces in, 347-363 , 371
internationalization in, 21
Internet use of, 3 , 10
interpreters for, 4 , 7-8 , 22
Java beans and, 20 , 22 , 620-626
java command in, 22
Java Development Kit (JDK) for, 5-6 , 5, 22 , 82 , 591-593 , 615-617
Java Runtime Environment (JRE) and, 8
Java virtual machine (JVM) and, 8
javac compiler for, 22 , 27-28 , 27, 77
JavaDoc comments for, 81-88 , 87, 81 , 119
JavaScript and, 18-20 , 23
JavaServer Pages (JSPs) in, 20 , 22 , 23 , 634-642
JBuilder for, 5 , 737 , 741
keywords in, 88-89
labeled statements in, 91 , 96-97
language of, 8-9
libraries in, 4
licensing of, 6
Linux support for, 6
local variables in, 31
loops in, 91 , 96-97 , 122 , 133-149
main method in, 69-71 , 77
method invocation expressions in, 91
methods in, 21 , 31 , 34-35 , 39 , 40 , 45-49 , 60-62 , 77 , 90
nested ifs in, 127
null expressions in, 116
Oak as early name for, 2-3
object model of., 680-681 , 681
object-oriented (OO) COBOL vs., 679-691 , 691
object-oriented nature of, 2 , 21
opening files in, 613-614
operators in, 92-93 , 98-115 , 119-120
OS/2 support for, 6
Java , continued
OS/390 support for (See also OS/390 and Java), 5 , 6 , 18 , 22
OS/400 support for (See also OS/400 and Java), 5 , 6 , 18 , 22
overloading methods in, 46-47 , 78 , 218 , 262
packages in, 50-57 , 50, 70 , 602-603
persistence in, 597
platform of, 601-602 , 601
portability of code in, 3 , 21
porting to various operating systems of, 6 , 8 , 18
pre/post increment/decrement expressions in, 91 , 103-106
properties in, 618-619
prototyping in, 47-48
reading files in, 614-615
releases and versions of, 5-6
Remote Method Invocation (RMI) and, 593-597
reuse of code in, 49-57 , 315-316 , 371-372
roles of, 4
running applications in, 8 , 8, 43 , 44
scoping in, 62-63
security in, 9
semicolon to end statements in, 34 , 80 , 93
sequence structures in, 121
serialization of objects in, 597-600
Servlets in, 3 , 16 , 18 , 22 , 23 , 626-634
statements in, 80 , 91 , 92-98
static initializers in, 590-591
string support in, 21
strongly typed variables in, 162
switch in, 128-132 , 159
syntax of (See also syntax of Java), 21 , 32-35 , 79-120 , 585-656
system.exit, 122 , 160
threads in, 21 , 395-435
transfer structures in, 91 , 122 , 150-159
upgrades/updates to, 5-6 ,
user profiles in, 618-619
user-defined functions (See methods), 21
user interface for, 21 , 437-513
variable declaration statements in, 91
variables in, 34 , 40 , 58-60 , 59, 77 , 88-90 , 119 , 590-591
vectors in, 21 , 199 , 221-229
Visual Cafe for, 5
VisualAge for, 5 , 22 , 23 , 27 , 81 , 373 , 647-656
VM support for, 6
Web browser use of, 3 , 10
WebSphere and, 16 , 22 , 23 , 373 , 626-644
while clause in, 95 , 96-97
working directory in, 604-606
writing to files in, 613-614
Java beans, 20 , 22 , 620-626
bound properties of, 622
customization (grinds) of, 625-626
events in, 622
features of, 621 , 622
functionality of, 620-621
get methods and, 622-623
introspection in, 622-623
methods in, 622
OS/400 and Java, 759
packages for, 603
properties of, 622
sample inventory application using, 624-625 , 624, 625
set methods and, 622-623
thought processes before using, 623
Java client APIs for OS/390 and Java, 705
java command in Java, 22
Java Customer Information Control System (JCICS)
COBOL database access and, 518
OS/390 and Java, 694 , 714-723
Java Database Connectivity (JDBC), 533 , 533
COBOL database access and, 518
drivers for, 738
JDBC/ODBC bridge, for Java/SQL database access, 533 , 534
OS/400 and Java, 738
Java Development Kit (JDK), 5-6, 5, 22, 82
exception classes in, 383-385 , 383, 384
OS/390 and Java, 696-704
OS/400 and Java, 727 , 728-729
Java Foundation Classes (JFC) in Swing, 504-505
Java Naming and Directory Services (JNDS)
in Enterprise Java Beans (EJBs), 643
in Java/SQL database access, 575
Java Native Interface (JNI), 534 , 591-593 , 615-617
Java Record IO (JRIO) for OS/390, 694 , 713
Java Runtime Environment (JRE), 8
Java Servlet Web Development Kit (JSWDK) for, 627
Java Transaction Services (JTS) in Java/SQL database access, 575
Java Transformer for OS/400 and Java, 736
Java virtual machine (JVM), 8
java.applet, 602
java.awt, 602
java.awt. print, 603
java.awt.dnd, 603
java.awt.event, 602
java.awt.font, 603
java.awt.geom, 603
java.awt.im, 603
java.awt.image.renderable, 603
java.beans.beancontext, 603
java.image, 602
java.io, 602
java.lang, 602
java.lang.ref, 603
java.math, 602
java.net, 602
java.rmi, 602
java.rmi.activation, 603
java.security, 602
java.security.cert and spec, 603
java.sql package, 533 , 536-537 , 602
java.text, 602
java.util, 602
java.util.jar, 603
JAVA_OBJECT in Java/SQL database access, 572
javac compiler, 22 , 27-28 , 27, 77
JavaDoc comments, 81-88 , 87, 81 , 119
JavaKey tool to sign applets, 495
JavaScript, 18-20 , 23
JavaServer Pages (JSP), 20 , 22 , 23 , 634-642
directives for variables in, 635
example of returning current directory contents using, 638-639
HTTP Server configuration using, 637-638 , 637
Java user interface and, 438
OS/390 and, 694
OS/400 and, 728
VisualAge for Java support in, 655
JavaSoft, 6
javax.server package for, 627
javax.swing, 603
javax.swing.xxx, 603
JCICS (See Java Customer Information Control System)
JDBC (See Java Database Connectivity)
jobs in OS/400 and Java, 739
jobs vs. threads, 396-398
join method, 431
Julian calendar vs. others in COBOL date/time, 280 , 281


 Team-Fly

Index

K

keyed files in OS/400 and Java, 756-758
KeyListener for Java user interface, 483 , 491
keys method in Java hashtable, 232
keywords in COBOL, 88
keywords in Java, 88-89


 Team-Fly

Index

L

Label in Java user interface, 441-442 , 482
labeled statements in Java, 91 , 96-97
language access through COBOL, 22
Language Environment (LE)
COBOL, 658
OS/390 and Java, 714
language of Java, 8-9
language-specific formatting in Java user interface , 499-504 , 505
lastElement method, 223
lastIndexOf method, 223
layout managers for Java user interface, 440 , 449-467 , 506
leap years in Java, 311-312 , 314
left-to-right rule in Java arrays, 213-214 , 239
LENGTH, 246 , 258-259
length method, 246 , 259
level-66/level-88 data items in COBOL, 170
libraries in Java, 4
licensing Java, 6
linkage editing (See binding)
LINKAGE SECTION, 44 , 72 , 156 , 168 , 172-173 , 664
Linux and Java, 6 , 656
List in Java user interface, 441-442 , 483 , 506
listeners for events in Java user interface, 475-476
literals in COBOL, 178-181
literals in Java, 178-181
local data items in COBOL, 30
local SQL, 529-530
LOCAL STORAGE, 30 , 57 , 168 , 172 , 174
local variables in Java, 31 , 172 , 174
locales in COBOL, 668
locales in Java user interface and, 499-504
locking in threads, 426-427 , 429-430 , 429
logical files
in COBOL database, 517
in OS/400 and Java, 729
logical operators in COBOL, 106-109
logical operators in Java, 106-109
Long class in Java, 192-194
long data type in Java, 164 , 165 , 166 , 192 , 195 , 197
longRun method, 410-411
look and feel selection for Java user interface, 508-509 , 509
loops in COBOL, 122 , 133-149
PERFORM, 134
PERFORM...TIMES, 122
PERFORM...UNTIL, 122 , 133 , 143-147
PERFORM...UNTIL...WITH TEST AT END, 133 , 147-149
PERFORM...VARYING, 122 , 133 , 134-135
PERFORM...WITH TEST AFTER...UNTIL, 122
loops in Java, 91 , 96-97 , 122 , 133-149
break statement in, 96-97 , 150-155 , 160
continue statement in, 96-97 , 150-155 , 160
do...until, 133 , 147-149 , 160
do-while, 122 , 133 , 147-149 , 160
exit in, 159
for loop, 97-98 , 122 , 133 , 134-143 , 160
return statement, 157-159 , 160
while, 96-97 , 122 , 133 , 143-147 , 160
LOWER-CASE, 246


 Team-Fly

Index

M

main method in Java, 44 , 69-71 , 77
main thread of execution, 405
make files, Java lack of, 8
mapping in COBOL user interface, 437
marshaling and RMI, 593
mathematical functions (Math class) in Java, 116-119 , 120
max, 118
members in Java classes, 40-43
memory management
COBOL programs, 39
garbage collection in Java and, 9
in Java, 8 , 9 , 39
Menu in Java user interface, 467-469 , 506
MenuBar in Java user interface, 467-469
MenuItem in Java user interface, 467-469 , 482 , 483
Message Queue Series (MQSeries)
COBOL database access and, 519
OS/390 and Java, 703
OS/400 and Java, 737 , 747 , 760
messages in OS/400 and Java, 740
meta classes in Java, 681
metadata, 528-529 , 563-568
method call expressions in Java, 116 , 122
method invocation expressions in Java, 91
methods in Java, 21 , 31 , 34-35 , 39 , 40 , 45-49 , 77 , 90 , 369 , 374
abstract, 342-343
access control in, 65
braces in, 35
calling, 45-46 , 46
calling, with interfaces, 350
case-sensitivity of, 45
class methods, 61
compile time vs. runtime in, 333-334
constructors as, 66-69 , 78 , 336 , 345-346
data types for, 34
declaring, 34 , 47
defining, 47 , 48-49
design patterns in, 328-334 , 372 , 376
dot operator to call, 40 , 77
dynamic class loading in, 47
empty parameters in, 45
encapsulation and, 64 , 317-319 , 317
expressions containing, 45
factory method patterns in, 328-334 , 372 , 376
final, 336-337
instance variables and, 61-62
interfaces in, 347-363 , 371
invoking, 688 , 690
Java beans, 622
Java user interface and, 443
main method in, 44 , 69-71 , 77
method call expressions in, 116
method invocation expressions in, 91
modifiers for, 34 , 35 , 48 , 65 , 78 , 335-336
naming, 35 , 45
object factories for, 328-334 , 372 , 376
object reference variable in, 77
object-oriented COBOL and, 686-688
overloading of, 46-47 , 78 , 218 , 262
overriding of, 323-325 , 336 , 365-366 , 496-497 , 680
package modifier in, 335-336
parameter lists for, 35
parameter passing in, 45 , 48
polymorphism in, 327-334 , 338-342
private modifier in, 335-336
protected modifier in, 335-336
prototyping of, 47-48
public modifier in, 335-336
qualifying methods and variables in, 41-42
restricting access to, 335-336
return statements for, 35
returned values from, 45
signature in, 45 , 70 , 347 , 348 , 350 , 391 , 393 , 680
static, 60-62
syntax for, 34-35
type of variable in, 35
type-name pairing in, 35
methods in object-oriented programming, 369 , 374
min, 118 , 119
modifiers in Java, 33-35 , 48 , 65 , 78 , 170-171 , 175 , 195 , 335-336 , 356 , 375
modules in COBOL, 26 , 28 , 28 , 671
modulus (See operators in COBOL; operators in Java )
MOVE, 102 , 121 , 188-192 , 191 , 246 , 257-258 , 263-264 , 283-284
multidimensional arrays in Java, 206-210 , 239
MULTIPLE, 101
multiple inheritance (See also interfaces), 346-347
multiplication (See operators in COBOL; operators in Java )
mutators (setters) in Java, 66


 Team-Fly

Index

N

named iterators in SQLJ, 578
negation in operators, and DeMorgen's rule, 109
nested COPY in COBOL, 668
nested ifs in COBOL, 127
nested ifs in Java, 127
nested programs in COBOL, 29 , 77 , 660 , 660, 668
nested variables in Java, 170
net drivers, in Java/SQL database access, 534
Netscape Communications Corporation, 2
network printing in OS/400 and Java, 739
new method, 202
NEXT SENTENCE, 96
NOT, 90 , 107-109
notifyAll method, 427
null values in COBOL, 668
null values in Java, 116 , 252 , 562
null values in object-oriented COBOL and, 689
NULL/NULLS, 181
numeric conversion for data types, 190-192
numeric data item in COBOL, 162 , 181-183
numeric data type in Java, 165 , 180 , 181-183 , 195-197
numeric-edited data item in COBOL, 162
NUMVAL, 247


 Team-Fly

Index

O

Oak, as early name for Java, 2-3
object factories, 328-334 , 372 , 376
object models
for Java, 680-681 , 681
for object-oriented COBOL, 684 , 684
OBJECT REFERENCE COBOL data type, 167
object reference data item in COBOL, 163
object reference variable in Java, 37-38 , 38 , 40-42 , 59 , 77 , 167 , 243-144 , 266 , 277
casting of, 364-365
object references in object-oriented COBOL and, 688
object-oriented (OO) COBOL, 45 , 658 , 679-691
classes in, 679 , 682-686 , 688-691
CORBA and, 683
Data Division in, 685 , 687
Environment Division, 684-685 , 687
freeing or deallocating objects in, 690-691
Identification Division in, 684 , 686-687
inheritance in, 679
instantiation in, 679 , 689-690
interface definition language (IDL) and, 683
invoking methods in, 688 , 690
Java object model vs., 680-681, 681, 691
methods in, 686-688 , 690
naming in, 689
null values in, 689
object model in, 684 , 684
object references in, 688
overriding methods in, 679
Procedure Division in, 685 , 687
SOMClass in, 683
System Object Model (SOM) for, 683
object-oriented nature of Java, 2 , 21
object-oriented programming, 315-376
business objects in, 372-373 , 376
casting objects in, 363-365
child classes in, 322-323 , 331 , 374
classes in, 369-370
compile time vs. runtime in, 333-334
containment in, 369 , 370
deep inheritance in, 326-327 , 326
design patterns, 328-334 , 372 , 376
designing applications using, 366-371 , 373-375
encapsulation in, 316, 317-319, 317, 375
extending classes in, 375
Extensible Markup Language (XML) and, 644-647
factory method patterns, 328-334 , 372 , 376
frameworks in, 372 , 376
has a relationships in, 370 , 375
inheritance in, 316 , 319-327 , 334-347 , 369 , 375
instance variables in, 375
interfaces in, 347-363 , 371 , 375
is a relationships in, 369-370 , 369 , 373 , 375
methods in, 369 , 374
modifiers and, 375
object factories, 328-334 , 372 , 376
object-oriented (OO) COBOL and, 679-691
objects in, 367-369
parameters vs. hard-coded values in, 372
persistence in, 597
polymorphism in, 316 , 327-334 , 338-342 , 349 , 375
relationships in, 369 , 370 , 375
reuse of code in, 315-316 , 371-372
serialization of objects in, 597-600
singletons, 420
static variables in, 375
usage in, 369
uses relationships in, 370 , 375
objects as Java classes, 36 , 38
objects in object-oriented programming, 367-369
occurrences in COBOL tables, 198
OCCURS, 203
OCCURS DEPENDING ON, 221
OMITTED, 47
ON EXCEPTION, 122 , 378
ON OVERFLOW, 122
ON SIZE ERROR, 122 , 378
one-based positioning in COBOL ,
arrays, 205
strings, 255
one-dimensional arrays in Java, 200
Open Transaction Manager Access (OTMA), COBOL
database access and, 519
opening files in Java, 613-614
operators in COBOL, 90 , 98-115
arithmetic operators in, 100-101
assignment operators in, 101-103 , 119
binary operators in, 99 , 100-101
logical operators in, 106-109
negation in, and DeMorgen's rule, 109
precedence of, 90 , 114-115
relational operators in, 106-109
unary operators in, 99 , 105 , 106
operators in Java, 92-93 , 98-115 , 119-120
arithmetic operators in, 100-101
assignment operators in, 37 , 95 , 101-103 , 119
binary operators in, 99 , 100-101 , 110-112
conditional operators in, 113-114 , 132-133
contraction of, 102-103 , 120
inverse operator (~) in, 112
logical operators in, 106-109
mathematical functions (Math class) in, 116-119 , 120
negation in, and DeMorgen's rule, 109
precedence of, 90 , 114-115
relational operators in, 106-109
shift operators in, 112-113
ternary operator (See conditional operator)
unary operators in, 99 , 105 , 106
OR, 90 , 107-109
OR, bitwise operators in Java, 110-112
org.omg.CORBA, 603
org.omg.xxx, 603
ORGANIZATION IS, 520
OS/2 and Java, 6 , 656
OS/390 and Java, 5 , 6 , 18 , 22 , 693-726 , 714
Abstract Windowing Toolkit (AWT) for, 703-704
batch processing in Java for, 703
CICS, 703 , 704 , 707 , 722-725
CICS classes and transactions in, 714-722
CICS Transaction Gateway and, 694
CLASSPATH setting in OMVS shell for, 701-703
Common connector framework (CCF) in, 705 , 706-708 , 707
connectors, 705
copying Java files in, 700-701
CORBA support, 703 , 722-725
Domino Go Webserver and, 694
Enterprise Access Builders (EAB) in, 705 , 708-712 , 710
Enterprise Java Beans (EJBs) and, 695
entry sequenced data sets (ESDS) in, 713
gateways, 704 , 705
hierarchical file system (HFS) in, 697-699
High Performance Java (HPJ) and, 694 , 714 , 725
HTTP server and, 694
IIOP, 722-725
IMS gateways, 703 , 704 , 707
IMS TCP/IP OTMA connection (TOC) in, 694
IMS TOC Connector for Java and, 694
installing components of, 695-696
IShell interpreter in, 697-699
Java client APIs, 705
Java Development Kit (JDK) and, 696-704
Java Record IO (JRIO) and, 694 , 713
JavaServer Pages (JSP) and, 694
JCICS, 694 , 714-723
Language Environment (LE), 714
linked program with passed integer value in, 721-722
MQSeries support, 703
Remote Abstract Windowing Toolkit (RAWT) for, 703-704
remote method invocation (RMI) support, 703
root directories in, 697
running Java classes in, 699-700
SAM Files, 704
Servlets and, 694
sockets support, 703
static compiler for Java, 28
threads and, 417
UNIX System Services Shell for, 693
user and group IDs for, 698
VisualAge for Java and, 694 , 705 , 721-722 , 724-726
VSAM, 713
Windows commands vs. IShell in, 698-699
OS/400 and Java (See also AS/400 Toolbox for Java), 5 , 6 , 18 , 22 , 727-760
Abstract Windowing Toolkit (AWT) for, 736-737
Access Builder for Domino for, 760
AS/400 Developer Kit for Java for, 727 , 728-729
AS/400 Toolbox for Java and, 728 , 737-758
AS400 object in, 739
calls in, 738
CICS support, 760
CL commands for Java in, 735-736
classes in, 741-743 , 741
CLASSPATH setting for, 733-735
CODE/400 support in, 735 , 737 , 741
command call in, 738
CommandCall class in, 742 , 743
Common Connector Framework (CCF) for, 760
compiling in, 759
conversion classes for, 746 , 747
Copy to Stream File (CPYTOSTMF), 732
copying Java files in, 732
CORBA, 737
data access in, 738
data areas in, 738
Data Description Specifications (DDS) for, 750-752
data queues in, 738
data types in, 740
DataQueue class in, 747-750
debugging in, 759
digital certificates in, 739
direct execution compiler for Java, 28
Encina support, 760
entering QShell Interpreter, 732
Enterprise Access Builder (EAB) for, 760
Enterprise Java Beans (EJBs) and, 728
exceptions in, 740
exporting classes in, 759
field description classes in, 750-751
FTP in, 733
GUI classes for, 740-741
helper classes, AS/400 Toolbox and, 739-740
Host On Demand support, 760
HTTP Server and, 728
IIOP, 737
input/output in, 747-750
installation of, 728
integrated file system (IFS) and, 729-730 , 738
J/Builder for, 737 , 741
Java beans in, 759
Java Developer Kit (JDK) for, 727 , 728-729
Java functions, AS/400 toolbox, 738-739
Java Transformer for, 736
JavaServer Pages (JSPs) and, 728
JDBC driver for, 738
jobs in, 739
keyed files in, 756-758
logical files in, 729
Message Queue Series (MQ) and, 737 , 747 , 760
messages in, 740
network printing in, 739
permissions in, 739
physical files in, 729
profile files in, 734-735
Program Call MetaLanguage (PCML) for, 757
program calls in, 738
ProgramCall class in, 742 , 743-746
ProgramParameter in, 744-746
QShell Interpreter for, 727 , 729-732 , 735-736
QSYS file system in, 740
Record class in, 754
record formats in, 740
RecordFormat class in, 754
record-level access in, 738 , 753-758
Remote Abstract Windowing Toolkit (RAWT) for, 736-737
remote compile, 759
remote debug, 759
Remote Method Invocation (RMI) in, 737
remote run, 759
root directory in, 729
RUNJVA command for, 735-736
running Java classes in, 732
running programs in, 759
SAP R/3 support, 760
Secure Sockets Layer (SSL) support, 742-743
Servlets and, 728
SmartGuide for, 745 , 759
Swing GUI components in, 740-741
system name for, 742
system status in, 739
system values in, 739
threads and, 417
trace in, 740
user IDs for, 742
user space in, 738
users and groups in, 739
VisualAge for Java and, 728 , 737 , 741 , 745 , 758-760
WebSphere and, 728
Windows commands vs. QShell in, 730-732
working with, 732
wrapper classes in, 745-746
OUTPUT, 520
overloading methods in Java, 46-47 , 78 , 218 , 262 , 323-325 , 336 , 365-366 , 496-497 , 680
overloading operators, Java lack of, 9
overriding methods in object-oriented COBOL and , 679


 Team-Fly

Index

P

pack method in Java user interface, 452-453 , 453, 454 , 454, 455 , 456
packages in Java, 33, 50-57, 50, 70, 602-603
user interface and, 505
access rights to, 64-65
calling, 70
CLASSPATH environment variable in, 54-56 , 77
default or unnamed, 52 , 53
defining, 51 , 51, 52
distribution of, 57
encapsulation and, 64
file system and, 53-54 , 54
importing, 52
JARing, 55-56 , 77
naming, 53
preparing for distribution of, 55-56
reasons for use of, 51
references to classes in, 52
use of, 52
ZIPping, 55 , 56 , 77
Panel in Java user interface, 440 , 445 , 462-467 , 505
paragraphs in COBOL, 29 , 32 , 77 , 89
parameter lists for Java methods, 35
parameter passing in COBOL, 48 , 172-173 , 233-238 , 668
parameter passing in Java, 45 , 48 , 72-76 , 172 , 233-238 , 239 , 172
serialization of Java objects and, 597-600
parameter variables in Java, 172
parameters vs. hard-coded values in object-oriented
programming, 372
parent Java classes, 320-321
parsing strings in COBOL and Java, 266
pass-by-content, 233-237
pass-by-reference, 48 , 234-237 , 668
pass-by-value, 48 , 233-237 , 239 , 668
PERFORM, 90 , 122 , 134 , 675
PERFORM...TIMES, 122
PERFORM...UNTIL, 122 , 133 , 143-147
PERFORM...UNTIL...WITH TEST AT END, 133 , 147-149
PERFORM...VARYING, 122 , 133 , 134-135
PERFORM...WITH TEST AFTER...UNTIL, 122
performance of COBOL, 22
Perl, 16
permissions in OS/400 and Java, 739
persistence
in Enterprise Java Beans (EJBs), 643
in Java, 597
in VisualAge for Java, 655
physical files in OS/400 and Java, 729
PIC coding of COBOL date/time, 280 , 285
PICTURE, 168 , 181 , 182
pixels to size Java user interface, 445
platform of Java, 601-602 , 601
plus sign operator to concatenate Java strings , 242-245 , 249
POINTER COBOL data type, 167
pointer data item in COBOL, 163
pointer variables, Java lack of, 8 , 9
polymorphism, 316 , 327-334 , 338-342 , 375
casting objects and, 364
design patterns, 328-334 , 372 , 376
factory method patterns, 328-334 , 372 , 376
interfaces and, 349
object factories, 328-334 , 372 , 376
PopupMenu class in Java user interface, 469-471 , 471
portability of Java code, 3 , 21
porting Java to various operating systems, 6 , 8 , 18
positioned iterators in SQLJ, 578
positioned update or delete in SQL, 525 , 554
positioning windows in Java user interface, 445
pow, 119
pre/post increment/decrement expressions in Java, 91 , 103-106
precedence of operators in COBOL, 90 , 114-115
precedence of operators in Java, 114-115
predicates in SQL, 522
pre-link step to DDL in COBOL, 661
PreparedStatement, Java/SQL database access, 547-548 , 556 , 558-559
primitive variables in Java, 164 , 195 , 197
using arrays instead of, to pass by reference , 237-238
priorities in threading, 430-431
private modifier, 335-336
private variables in Java, 171 , 175 , 195
PROCEDURE DIVISION, 28 , 30 , 44 , 69 , 71 , 72 , 156 , 235 , 248 , 660 , 663 , 672 , 675
Procedure Division class in object-oriented COBOL and, 685 , 687
PROCEDURE POINTER COBOL data type, 167
procedure pointer data item in COBOL, 163 , 668
procedures, ILE, 672 , 675
PROCESS, 90
profile files in OS/400 and Java, 734-735
Program Call MetaLanguage (PCML), 757
program calls in OS/400 and Java, 738
PROGRAM ID COBOL, 30
program objects in COBOL, 671
ProgramCall class in OS/400 and Java, 742 , 743-746
ProgramParameter in OS/400 and Java, 744-746
programs in COBOL, 32 , 39 , 44 , 89-90
properties in Java, 618-619
Java beans, 622
protected modifier, 335-336
protected variables in Java, 171 , 195
prototyping Java methods, 9 , 47-48
proxies and RMI, 593
public modifier, 335-336 , 335
public variables in Java:, 171 , 175 , 195
push button in Java user interface, 441 , 441
put method in Java hashtable, 232


 Team-Fly

Index

Q

QShell Interpreter for OS/400 and Java, 727 , 729-732 , 735-736
QSYS file system for OS/400 and Java, 740
qualified names in COBOL, 62-63
qualified names in Java, 62-63 , 175 , 195
qualifying methods and variables in Java, 41-42 , 41
queries in Java/SQL database access, 556 , 559-560
queued sequential access method (QSAM), 516 , 517
quotation marks to delimit COBOL strings, 244 , 245
quotation marks to delimit Java strings, 245
QUOTE/QUOTES, 181


 Team-Fly

Index

R

radiobutton in Java user interface, 506
random, 119
RANGE, 205
ranges for numeric data type in COBOL and Java, 181-183
READ, 519 , 520
reading files in Java, 614-615
Record class in OS/400 and Java, 754
record formats
COBOL database access and, 517
OS/400 and Java, 740
RECORD KEY, 520
RecordFormat class in OS/400 and Java, 754
record-level access
COBOL database access and, 516 , 517 , 519-521
OS/400 and Java, 738 , 753-758
records in COBOL database, 517
recursive calls in COBOL, 658
REF, Java/SQL database access, 572
reference variables in Java, 37 , 164 , 195 , 197 , 211-213
referential integrity (RI) constraints in Java/SQL
database access, 568 , 569
regionMatches, 276
relational operators in COBOL, 106-109
relational operators in Java, 106-109
relationships in object-oriented programming, 369 , 370 , 375
releases and versions of Java, 5-6
REM, 101
remainder (See operators in COBOL; operators in Java)
Remote Abstract Windowing Toolkit (RAWT)
OS/390 and Java, 703-704
OS/400 and Java, 736-737
remote interfaces for Enterprise Java Beans (EJBs) , 643-644
Remote Method Invocation (RMI), 593-597
Java/SQL database access, 534
in OS/390 and Java, 703
in OS/400 and Java, 737
packages for, 603
thread support in, 417
remote procedure calls (RPC), 593
Remote Unit of Work (ROW) SQL, 530
remove method, 232 , 483
removeAll, 483
removeAllElements method, 223
removeElementAt method, 223
replace method, 246 , 259-263 , 267
replaceItem, 483
replaceRange, 483
repository for Java in VisualAge, 649-650
requestFocus, 443
ResourceBundle subclass for locales in Java user
interface, 501-504
result sets, Java/SQL database access, 550-554 , 556 , 573-574
ResultSet, 537 , 573-574
ResultSetMetaData interface for, 537 , 566-567
resume method, 431
return statement, 35 , 48 , 122 , 160 , 157-159
RETURNING, 29 , 44 , 156-157
reuse of code, 49-57 , 315-317 , 371-372 , 673-674 , 676-677
REVERSE, 247 , 269
reverse method, 269
REWRITE, 520
rint, 119
roles of Java, 4
ROLL, ROLS, ROLU, 520
ROLLBACK, 520-521
root directories
in OS/390 and Java, 697
in OS/400 and Java, 729
round, 119
rows in COBOL database, 517
RUNJVA command for OS/400 and Java, 735-736
Runnable, 405-407 , 412 , 555
running Java applications, 8 , 8, 43 , 44
running programs in COBOL, 675
Runtime Exception class, 381-382
runtime initialization of Java arrays, 204-206 , 208 , 239
runtime vs. compile time in object-oriented
programming, 333-334


 Team-Fly

Index

S

SAM Files in OS/390 and Java, 704
sandbox for Java applets, 17 , 495
SanFrancisco, 372
SAP R/3 support for OS/400 and Java, 760
scheduler for threading, 430-431
schema in COBOL database, 517 , 544
scope of data items in COBOL, 62-63 , 175
scope of Java variables, 62-63 , 173 , 175
Scrapbook in, 655
Screen Design Aid (SDA) for COBOL user interface, 437
SEARCH, 90 , 217
search algorithm for Java arrays, 217-222
SEARCH...WHEN, 122
Searchable interface, 355
searched update or delete in SQL, 525 , 554
searches using Java interfaces, 352-356
sections in COBOL, 29 , 89
Secure Sockets Layer (SSL) support in OS/400 and Java, 742-743
security
firewalls and, 14
Java, 9
Java packages for, 602-603
select method, 519
Java user interface and, 482 , 483
Java/SQL database access, 549 , 556
SELECT in SQL, 522-524
selectAll, 483
semicolon to end statements in Java, 34 , 80 , 93
sentences in COBOL, 89
sequence structures in COBOL, 121
sequence structures in Java, 121
serialization of Java objects, 597-600 , 597
service programs in COBOL, 49-50 , 64 , 77 , 673-674 , 676-677
Servlet URLs, 627
Servlets in Java, 3 , 16 , 18 , 22 , 23 , 626-634
command beans and, 642
common gateway interface (CGI) and, 626
destroy in, 628
doGet in, 628
doPost in, 628 , 630
example of Web page listing directory contents using, 629-630
getServletInfo in, 628
HTML page generated using, 630
hypertext markup language (HTML) and, 626-634
init method in, 628
Java Servlet Web Development Kit (JSWDK) for , 627
Java user interface and, 438
javax.server package for, 627
methods for, 628
OS/390 and, 694
OS/400 and, 728
processing HTML form information using , 631-634 , 631, 633
Servlet URLs, 627
thread support in, 417
VisualAge for Java and, 655
session beans as Enterprise Java Beans (EJBs), 643
SET, 102 , 121
set methods
input processing in Java user interface and , 482-484
Java beans, 622-623
Java/SQL database access, 548-550
set Title, 445
setBackground, 443 , 446
setCaretPosition, 483
setEditable, 483
setElementAt, 223
setEnable, 443
setFont, 443 , 444 , 446
setForeground, 443 , 446
setLabel, 482 , 483
setLocation, 445
setSelectedCheckbox, 482
setSize, 223 , 445
setState, 482
setters (mutators) in Java, 66
setText, 482 , 483
SETU/SETS, 520
setVisible, 443 , 473
severity of Java exceptions, 380-381
shift, bitwise operators in Java, 110-113
Short class in Java, 192-194
short data type in Java, 164 , 165 , 166 , 195 , 197
shortcuts for menu items in Java user interface , 472-473 , 473, 506
signatures, digital signatures for Java applets, 17
signatures, method signatures in Java, 45 , 70 , 347 , 348 , 350 , 680
exception throwing and, 391 , 393
SimpleDateFormat class, 290 , 303-306 , 308
sin, 119
singletons, 420
size method, 223 , 232
sizing of windows in Java user interface and, 445
Smalltalk, 3 , 8
SmartGuide for OS/400 and Java, 745 , 759
SmartTextField, 512-513
sockets support for OS/390 and Java, 703
software loading instructions, 761-763
SOMClass in object-oriented COBOL and, 683
sorting Java arrays, 219-220
Source Entry Utility (SEU) and Java, 27
SPACE/SPACES, 90 , 181
spawning threads, 398-407
SQL and COBOL database access, 517 , 518 , 522-532
application programming interfaces (APIs) and , 528-529
call level interfaces (CLIs) and, 528-529
CLOSE, 525 , 554
cursors in, 525 , 553-554
data definition language (DDL) for, 530 , 531
data manipulation language (DML) for, 530 , 531
data types, Java vs. SQL, 561-562
DELETE, 523 , 524
deleting rows in, 523
Distributed Relational Database Architecture (DRDA), 530
distributed SQL, 529-530
Distributed Unit of Work (DOW), 530
dynamic SQL, 524 , 527
embedded SQL, 524-526 , 527 , 528-529
error checking and warning codes in, 526
EXEC SQL/END-EXEC, 524
filter criteria in, 522 , 523
FROM clause, 522-523
GROUP BY clause, 523
HAVING clause, 523
indexes in, 530
INSERT, 524
inserting rows, 524
interactive vs. embedded, 524-526
local SQL, 529-530
metadata and, 528-529
packages for, 602
positioned update or delete in, 525 , 554
predicates in, 522
Remote Unit of Work (ROW), 530
searched update or delete in, 525 , 554
SELECT in, 522-523 , 524
static SQL, 527
synchronizing transactions in, 530
UPDATE, 523 , 524
updating rows in, 523
views in, 530
WHERE clause, 522-523
SQLJ
COBOL database access and, 518
Java/SQL database access, 576-582
VisualAge for Java, 655
sqrt, 119
SSRANGE, 205
standard output in Java, 74-76 , 225
START, 520
startsWith, 276
Statement class, 537 , 547-548 , 556 , 558-559 , 558
statements in COBOL, 89 , 90
statements in Java, 91 , 92-98
assignment expressions in, 91 , 101-103
block statements in, 91 , 93-95
braces to delimit blocks in, 80 , 93 , 173
class instance creation expressions in, 91
decision statements in, 91
empty statements in, 91 , 95-96
expression statements in, 91 , 92
labeled statements in, 91 , 96-97
loop statements in, 91
method invocation expressions in, 91
operators in, 92-93
pre/post increment/decrement expressions in, 91 , 103-106
semicolon to end, 80 , 93
transfer statements in, 91
variable declaration statements in, 91
static calls in COBOL, 659 , 663 , 671 , 675
static data items in COBOL, 57
static data items in Java, 57-62
static final variables in Java, 171 , 195
static initializers in Java, 590-591
static methods in Java, 60-62
static SQL, 527
static variables in Java, 58-60 , 59, 171 , 174 , 195 , 375 , 590-591
static Web pages with HTML, 13 , 15
stop method, 409-410 , 412
STOP RUN, 122 , 156 , 159
stored procedures in Java/SQL database access, 568 , 569-572
STRING, 246 , 247-248
StringBuffer class in Java, 266-271 , 277
strings in COBOL, 242 , 244-247
alphabetic vs. numeric testing for, 264-265
class condition test in, 264-265
comparing strings, 244
concatenation in, 247-249
convert upper/lowercase in, 263-264
defining, 244
extract individual words from, 274-276
INSPECT, 246
INSPECT CONVERTING, 260-264
INSPECT REPLACING ALL, 259-260
INSPECT TALLYING, 246 , 254-256 , 257-258
instantiation of, 244
Java vs., 246-247
LENGTH, 246 , 258-259
length of, 244 , 245 , 258-259
LOWER-CASE, 246
MOVE, 246 , 257-258 , 263-264
NUMVAL, 247 , 266
one-based positioning in, 255
parsing in, 266
quotation marks to delimit, 244 , 245
REVERSE, 247 , 269
search for substring in, 254-256
STRING, 246 , 247-248
substrings in, 249-250
translating characters in, 259-260
translating strings in, 260-263
trimming blanks in, 256-257
UNSTRING, 247 , 254-256 , 274
UPPER-CASE, 246
strings in Java, 21 , 59 , 165 , 166 , 167 , 195 , 241-277
alphabetic vs. numeric testing for, 265-266
append in, 268
BigDecimal in, 247 , 266
capacity of StringBuffer objects in, 269-271
charAt method in, 262
COBOL vs., 246-247
compareTo in, 276
comparing strings in, 243-244
concat in, 246
concatenation using append method, 268
concatenation with concat method, 243-244 , 248-249
concatenation with plus sign operation, 242-244 , 245 , 249
copyValueOf in, 276
declaring, with StringBuffer class, 267-268
dynamically changing strings using StringBuffer
class, 267-271
endsWith in, 276
equality testing using equals method in, 243-244 , 253
equals in, 276 , 277
equalsIgnoreCase in, 276 , 277
escape sequences in, 245
extending String class prohibited in, 261
extract individual words from, 274-276
get individual character from string (charAt), 262
getBytes in, 276
getChars in, 276
hashCode in, 276
immutability of String class and, 266-267 , 277
indexOf in, 246 , 256
insert method in, 268-269
inserting characters in, 268-269
intern in, 276
isDigit method in, 246 , 265-266
length of string in, 246 , 258-259 , 269-271
methods for, 245
mutability of StringBuffer class in, 267 , 277
names, toString method and, 251-254
null strings in, 252
as object reference variables, 243-244 , 266 , 277
parsing in, 266
quotation marks to delimit, 245
regionMatches in, 276
replace method in, 246 , 259-263 , 267
reversing order of (reverse method), 247 , 269
search for substring in, 255-256
startsWith in, 276
StringBuffer class for, 266-271 , 277
StringTokenizer for, 247 , 274-276 , 277
substrings in, 246 , 250-251
syntax and style for, 242-243
toCharArray in, 276
tokenizing strings in, 274-276
toLowerCase in, 246 , 264 , 277
toUpperCase in, 246 , 264 , 277
trim method in, 246
trimLeading blanks in, 271-274
trimming blanks in, 257-258 , 271-274
trimTrailing blanks in, 271-274
Unicode characters in, 259
valueOf in, 277
zero-based positioning in, 255
StringTokenizer, 247 , 274-276 , 277
strongly typed variables in Java and COBOL, 162
STRUCT, 572
Structure Query Language (See SQL)
subclass, 322
subprograms in COBOL, 71
subscripting, relative subscripting in COBOL tables, 206
substrings in COBOL, 249-250
substrings in Java, 246 , 250-251
SUBTRACT, 101 , 106
SUBTRACT-DURATION, 289 , 298
subtraction (See operators in COBOL; operators in Java )
SUING, 72
superclass, 322 , 376
superscript letters in WorkBench window of VisualAge for Java, 649 , 649
suspend method, 431
swap, 47 , 48 , 238
Swing for Java user interface, 438-439 , 504-513
adding components to panels in, 505
adding components to dialogs in, 505
adding components to frames in, 505
advanced support in, 509-510
applets in, 505
assistive technology with, 505
AWT vs., 504-506
button in, 506
checkbox in, 506
colors in, 506
drag-and-drop support in, 505
Dropdown in, 506
events in, 506
graphics in, 505
Input Method Framework in, 505
Java Foundation Classes (JFC) in, 504-505
language-specific formatting in, 505
layout managers for, 506
lists in, 506
look and feel selection in, 508-509 , 509
menus in, 506
OS/400 and, 740-741
packages for, 505 , 603
panels in, 505
radiobutton in, 506
sample program in, 507-508 , 508
shortcuts to menus in, 506
text constants in, 506
text-entry components in, 506
To-Do List example in, 511-512 , 511, 762
windows in, 505
switch, 122 , 128-132 , 159
Symantec, 5
synchronized keyword, 423-427
synchronized processing and threads, 395-396 , 396, 422-423 , 425-430 , 426
synchronizing transactions in SQL, 530
syntax of Java, 21 , 32-35 , 79-120 , 585-656
anonymous inner classes in, 588-590
arithmetic operators in, 100-101
array index expression in, 116
assignment expressions in, 91 , 101-103
assignment operators in, 37 , 95 , 101-103 , 119
binary operators in, 99 , 100-101
bitwise operators in, 110-112
block statements in, 80 , 91 , 93-95
braces to delimit blocks in, 80 , 93 , 173
break in, 96-97
calling programs and commands into, 615-617
class instance creation expressions in, 91
classes in (See classes in Java)
COBOL vs., 89-92
command beans and, 642
comments in, 80-88 , 119
computational expressions in, 92 , 98 , 99-115 , 119
conditional operators in, 113-114 , 132-133
continue in, 96-97
contracted operators in, 102-103 , 120
decision statements in, 91
directives for variables in, 635
directory creation in, 612-613
directory listing in, 606-611
double equals sign to compare variables in, 95 , 107-109
drive listing in, 611-612
empty block in, 96
empty statements in, 91 , 95-96
expression statements in, 91 , 92 , 98-115
Extensible Markup Language (XML) and, 644-647
fetch expressions in, 92 , 98-99 , 115-116 , 119
file creation in, 613-614
file listing in, 606-611
for in, 97-98
if condition in, 94-95
inner classes of, 586-588
inverse operator (~) in, 112
JavaDoc comments for, 81-88 , 87, 119
keywords in, 88-89
labeled statements in, 91 , 96-97
logical operators in, 106-109
loop statements in, 91 , 96-97
mathematical functions (Math class) in, 116-119 , 120
method call expressions in, 116
method invocation expressions in, 91
methods (See methods in Java)
null expressions in, 116
opening files in, 613-614
operators in, 92-93 , 98-115 , 119-120
persistence in, 597
pre/post increment/decrement expressions in, 91 , 103-106
precedence of operators in, 114-115
properties in, 618-619
reading files in, 614-615
relational operators in, 106-109
semicolon to end statements in, 80 , 93
serialization of objects in, 597-600
shift operators in, 112-113
statements in, 80 , 91 , 92-98
static initializers in, 590-591
strings (See strings in Java)
ternary operator (See conditional operator)
transfer statements in, 91
unary operators in, 99 , 105 , 106
user profiles in, 618-619
variable declaration statements in, 91
variable naming in, 88-89 , 119
variables in (See variables in Java)
while in, 95 , 96-97
white space in, 79-80
working directory in, 604-606
writing to files in, 613-614
System Object Model (SOM) for object-oriented
COBOL, 683
system status in OS/400 and Java, 739
system values in OS/400 and Java, 739
system.exit, 122 , 160


 Team-Fly

Index

T

tables in COBOL (See arrays in Java), 198-220 , 517
compile-time arrays in, 203-204 , 203
creating, 203
defining size of (See also vectors), 199-200 , 205-206 , 221
grouped items in, 214-215
instantiating, 203
occurrences in, 198
one-based nature of, 205
parameter passing in, 233-238
pass-by-content for parameters in, 233-237
pass-by-reference for parameters in, 234-237
runtime initialization of, 204-206 , 208 , 239
search algorithm for, 217
size of, defining (See also vectors), 199-200 , 205-206 , 221
subscripting, relative subscripting in, 206
three-dimensional array in, 210
ternary operator (See conditional operator)
TEST-DATE-TIME, 289
TestEvents class, 480-481
TestFrame, 447
text constants in Java user interface, 506
TextArea in Java user interface, 441-442 , 483
text-entry components in Java user interface, 506
TextField, 441-442 , 483 , 490
thin drivers in Java/SQL database access, 534
this predefined object variable in Java, 42
thrashing, 425
Thread class, 401 , 412
thread groups, 413-414
ThreadDeath class, 411-412 , 414
ThreadGroup class, 413-414
thread-pool, 425
threads and threading, 21 , 395-435
Java/SQL database access, 555-556
alternatives to, 425
asynchronous processing and, 395-396 , 396 , 401-407 , 416-425
Common Object Request Broker Architecture (CORBA) and, 417
daemon threads in, 415-416
deadlock in, 429-430 , 429
ending programs with running threads in, 415-416
extends Thread method for, 401-405 , 412
fine-grained synchronization for, 423-425
groups, thread groups, 413-414
instance variables shared by, 397-398
isAlive method for, 431
jobs vs., 396-398
join method for, 431
locking in, 426-427 , 429-430 , 429
longRun method for, 410-411
main thread of execution in, 405
multiple threads using shared object, 418-425
named threads in, 431
notifyAll method in, 427
operating system support for, 397-398
OS/390 support for, 417
OS/400 support for, 417
overhead of, 397
priorities in, 430-431
Remote Method Invocation (RMI) and, 417
resume method for, 431
Runnable interface for, 405-407 , 412
scheduler for, 430-431
Servlets and, 417
spawning of, 398-407
stop method for, 409-410 , 412
stopping, 407-412
suspend method for, 431
synchronized keyword and, 423-427
synchronized processing and, 395-396 , 396 , 422-423 , 425-430 , 426
thrashing in, 425
Thread class for, 401 , 412
ThreadDeath class for, 411-412 , 414
ThreadGroup class, 413-414
thread-pool of, 425
timing-dependency in, 421-425 , 422, 432-435
transaction threads, asynchronous, problems with, 416-425
wait method in, 427
wait queues in, 427-429 , 428
yield method for, 431
three-dimensional array in COBOL, 210
three-dimensional arrays in Java, 209-210 , 210
throw, 122 , 160 , 386-388 , 391-392 , 393
Throwable class, 379-381 , 381
TIME, 280 , 285 , 287
time (See date and time in Java ; date and time in COBOL)
Time class, 167 , 296
time data item in COBOL, 163
time zones, 294
TIMESTAMP, 285 , 287
Timestamp class, 296
timestamp data item in COBOL, 163
TimeZone class, 290 , 312-314
timing-dependency in threading, 421-425 , 422, 432-435
title of windows in Java user interface, 445
toCharArray, 276
To-Do List example for Java user interface and, 484-490 , 484, 511-512 , 511, 762
tokenizing strings in Java, 274-276 , 274
toLowerCase, 246 , 264 , 277
toString, 223 , 251-254 , 366
toUpperCase, 246 , 264 , 277
trace in OS/400 and Java, 740
transaction isolation in Java/SQL database access, 562-563
transaction monitoring in COBOL database access
and, 517
transaction threads, asynchronous, problems with, 416-425
transfer structures in COBOL, 122 , 150-159
CONTINUE, 153
EXIT, 152
EXIT PROGRAM, 156
GO TO, 152 , 156 , 160
GOBACK, 156
RETURNING, 156-157
STOP RUN, 156 , 159
transfer structures in Java, 91 , 122 , 150-159
break, 122 , 150-155 , 160
continue, 122 , 150-155 , 160
exit in, 159
return, 122 , 157-159 , 160
system.exit, 122
throw, 122 , 160
try/catch, 122 , 160
transferFocus, 443
transient variables in Java, 171-172 , 195
translating characters in COBOL, 259-260
translating strings in COBOL, 260-263
triggers for Java/SQL database, 568 , 569
trim method, 246
trimming blanks in COBOL strings, 256-257
trimming blanks in Java strings, 257-258 , 271-274
trimToSize method in Java vector, 223
true/false (See boolean)
try/catch, 122 , 160 , 388-391 , 393 , 538
two-dimensional arrays in Java, 201 , 207-208
TYPEDEF, 163 , 167 , 668
type-name pairing in Java methods, 35


 Team-Fly

Index

U

unary operators in COBOL, 90 , 99 , 105 , 106
unary operators in Java, 99 , 105 , 106
unbalanced arrays in Java, 213-214 , 239
UNDATE, 282 , 284
Unicode standards for char data type in Java, 165 , 195 , 259
uniform resource locators (URLs) and HTML, 13
UNIX System Services Shell for OS/390 and Java, 693
UNSTRING, 247 , 254-256 , 274
UPDATE, 523 , 524 , 550
upgrades/updates to Java, 5-6
upper/lowercase string conversion in Java and COBOL, 263-264
UPPER-CASE, 246
USAGE, 168
usage in Java classes, 369
USE, 122
USE AFTER ERROR, 379
user and group IDs for OS/390 and Java, 698
user interface in COBOL
CICS BMS maps in, 437
CODE Designer Tool for, 437
DDS display files in, 437
green screen, 437
IMS MFS maps, 437
ISPF panels in, 437
mapping, 437
Screen Design Aid (SDA) for, 437
user interface in Java (See also Abstract Windowing Toolkit (AWT); Swing), 21 , 437-513
Abstract Windowing Toolkit (AWT) for, 438-439 , 440-504
green screen vs., 439
hypertext markup language (HTML) for, 438
JavaServer Pages for, 438
OS/390 and AWT, 703-704
OS/400 and Java, 736-737 , 740-741
polymorphism example for, 338-342
Servlets for, 438
SmartTextField for, 512-513
Swing for, 438-439 , 504-513
To-Do list example, 484-490 , 484, 511-512 , 511 , 762
Visual Composition Editor (VCE) for, 440 , 512 , 652-654 , 653
VisualAge for Java and, 440 , 512
WYSIWYG tools for, 440 , 512
user messages in Java user interface 476-477 , 492-494
user profiles in Java, 618-619
user space in OS/400 and Java, 738
user-defined data types COBOL, 668
user-defined functions in Java (See methods)
users and groups in OS/400 and Java, 739
uses relationships, 370 , 375
USING, 41 , 44 , 45 , 47 , 69
USING BY VALUE, 235


 Team-Fly

Index

V

VALUE, 195 , 203
valueOf, 277
variable declaration statements in Java, 91
variables in COBOL, 88-89
variables in Java (See data items in COBOL), 31 , 36-40 , 77 , 88-90 , 119 , 161-195
access control in, 65
accessing, 41
arrays as (See arrays)
assigning expressions to, 93
BigDecimal data type in, 166 , 184-187 , 195 , 196
blocks in, 173
boolean data type in, 165 , 167 , 181 , 187-188 , 195 , 197
byte data type in, 164 , 165 , 197
case-sensitivity of, 170
casting, 188-192 , 195
char data type in, 165 , 166 , 180 , 195 , 197
class methods and, 61
class variables, 58-60, 59, 171, 172, 174, 195, 198 , 590
classes of, 170
CLASSPATH environment variable in, 54-56 , 77
data hiding in, 42-43 , 78
data types of, 164-167 , 197-198
date and time data types in, 167
Date class in, 167
decimal places in, 183-187 , 195 , 196
DecimalFormat class in, 166
declaring, 58 , 172
defining, 168-172
directives for, 635
dot operator for, 175
double data type in, 165 , 166 , 191 , 195 , 197
double equals sign to compare, 95 , 107-109
encapsulation in, 317-319 , 317
equals sign to equate, 37 , 95 , 101-103 , 119
example of use, 176-178
external access for, 175
float data type in, 165 , 166 , 180 , 183-187 , 191 , 195-197
getters (accessors) for, 66
initialization of, 60 , 169 , 178-181 , 195
instance variables in, 41 , 58-62 , 59, 66 , 77 , 375 , 397
int data type in, 164 , 165 , 166 , 192 , 195 , 197
interface variables in, 198
keywords in, 88-89
lifetime of, 174
literals in, 178-181
local, 172 , 174
long data type in, 164 , 165 , 166 , 192 , 195 , 197
modifiers for, 65 , 170-171 , 175 , 195
naming, 39 , 88-89 , 119 , 170
nesting, 170
numeric conversion for, 190-192
numeric data type in, 165 , 180-183 , 195-197
object reference variables in, 37-38 , 38, 40 , 41-42 , 59 , 77 , 167 , 243-244 , 266 , 277
parameter passing in, 172
parameter variables, 172
primitives, 164 , 195 , 197
private, 171 , 175 , 195
protected, 171 , 195
public, 171 , 175 , 195
qualified names for, 175 , 195
qualifying methods and, 41-42
ranges for numeric data type in, 181-183
reference variables in, 164 , 195 , 197
restricting access to, 43
scope of, 173 , 175
setters (mutators) for, 66
short data type in, 164 , 165 , 166 , 195 , 197
static final, 171 , 195
static, 58-60 , 59, 171 , 174 , 195 , 375 , 590-591
strings (See also char data type in Java ; strings in Java ), 59 , 165 , 167 , 195 , 241
strongly typed, 162
syntax of, 34
this predefined object variable in, 42
Time class in, 167
transient, 171-172 , 195
true/false (See boolean)
Unicode standards for char data type, 165 , 195
variable declaration statements in, 91
volatile, 171-172 , 195
wrapper classes and, 166 , 192-194 , 195
vectors in Java, 21 , 199 , 221-229 , 239
addElement method, 222
capacity method, 222
clone method, 222
contains method, 222
copyInto method, 222
declaring, 224
elementAt method, 222
elements method, 222
ensureCapacity method, 223
firstElement method, 223
indexOf method, 223
insertElementAt method, 223
isEmpty method, 223
lastElement method, 223
lastIndexOf method, 223
logic in, 224
removeAllElements method, 223
removeElementAt method, 223
setElementAt method, 223
setSize method, 223
size method, 223
standard output for, 225
toString method, 223
trimToSize method, 223
views in COBOL, 517
views in SQL, 530
virtual sequential access method (VSAM), 516-517 , 522 , 713
Visual Cafe for Java, 5
Visual Composition Editor (VCE), 440 , 512 , 652-654 , 653
VisualAge for COBOL, 5 , 6 , 81 , 763
VisualAge for Java, 5 , 22 , 23 , 27 , 81 , 373 , 644 , 647-656
browsers, class browsers for, 327 , 327
CD-ROM copy of, 762-763 , 763
Common Connector Framework (CCF) in, 706-708 , 707, 760
connectors and, 655
CORBA support in, 655
creating code in, 650 , 651
Data Access beans in, 655
debugging code in, 651-652 , 652
editing code in, 650-651
Enterprise Java Beans (EJBs) and, 655
Enterprise Toolkit in, 655
existing Java code and, 650
exporting code from, 650
functionality of, 654-655
high-performance compiler in, 655
importing code to, 650
Integrated Development Environment (IDE) and , 648
Java user interface and, 440 , 512
open package window in, 651 , 651
operating system support for, 656
OS/390 and Java, 694 , 705 , 721-722 , 724-726
OS/400 and Java, 728 , 737 , 741 , 745 , 758-760
persistence in, 655
repository for Java in, 649-650
Scrapbook in, 655
Servlet and JSP support in, 655
SQLJ and, 655
superscript letters in WorkBench window, 649 , 649
versioning of, 650
versions of, 648
Visual Builder (See Visual Composition Editor)
Visual Composition Editor (VCE) for, 440 , 512 , 652-654 , 653
workspace/WorkBench of, 649 , 649
VM support for Java, 6
volatile variables in Java, 171-172 , 195


 Team-Fly

Index

W

wait method, 427
wait queues in threading, 427-429 , 428
Web applications, 3 , 10
HTML and, 13
HTTP Server configuration, using JSPs, 637-638 , 637
JavaServer Pages (JSPs) in, 634-642
WebSphere for Java (See also applets; JavaServer Pages; Servlets), 16 , 22 , 23 , 373 , 626-644
command beans and, 642
Enterprise Java Beans (EJBs) and, 642-644
Java/SQL database access, 534 , 575
JavaServer Pages (JSPs) in, 634-642
OS/400 and Java, 728
Servlets in, 626-634
WHERE clause SQL, 522-523 , 522
while clause (See also do-while loop), 95-97 , 122 , 133 , 143-147 , 160
white space in Java, 79-80
WindowListener interface, 479-481
windows in Java user interface, 505
working directory in Java, 604-606
WORKING STORAGE, 29 , 30 , 32 , 39 , 44 , 57 , 58 , 61 , 72 , 168 , 172 , 174 , 178 , 248 , 664
workspace/WorkBench of VisualAge for Java, 649, 649
wrapper classes, 166, 192-195, 745-746
WRITE, 519 , 520
writing to command line in COBOL, 73-74
writing to command line in Java, 74-76
writing to files in Java, 613-614
WYSIWYG tools for Java user interface, 440 , 512


 Team-Fly

Index

X

XML (Extensible Markup Language), 644-647


 Team-Fly

Index

Y

YEAR-TO-YYYY, 282 , 283
YEARWINDOW, 282
YEAR-YYYY, 282
yield method, 431


 Team-Fly

Index

Z

ZERO, 90
ZERO/ZEROS/ZEROES, 181
zero-base of Java arrays, 205
zero-base of Java date/time, 295
zero-base of Java strings, 255
ZIP files and Java packages, 55 , 56 , 77


 Team-Fly

List of Figures

Chapter 1: The World of Java

Figure 1.1: The Java Development Kit (JDK).
Figure 1.2: Compiling Java source code to Java bytecode.
Figure 1.3: Running a Java application.
Figure 1.4: Netscape Navigator showing an HTML page.
Figure 1.5: Netscape Navigator displaying a hypertext link.

Chapter 2: Java Flow of Control

Figure 2.1: A primary class file usingmany additional class files.
Figure 2.2: Compiling, binding, and linking COBOL.
Figure 2.3: Compiling Java source with the javac com-piler
Figure 2.4: A typical compiled COBOL source pro-gram.
Figure 2.5: A high-level view of a class.
Figure 2.6: A high-level view of a method.
Figure 2.7: The syntax of a Java class.
Figure 2.8: Object reference variables pointing to separate objects.
Figure 2.9: Multiple object reference vari-ables pointing to the same object in memory.
Figure 2.10: Java flow in a typical application.
Figure 2.11: Linking the same reusable object to multiple program objects.
Figure 2.12: Defining a package containing classes for common data-structure algorithms.
Figure 2.13: Defining the package to which a class will belong.
Figure 2.14: A file directory structure.
Figure 2.15: Memory allocation for static and instance variables.
Figure 2.16: A comparison of COBOL and Java.

Chapter 3: Java Syntax

Figure 3.1: A generated HTML file.
Figure 3.2: The result of clicking the myMethod link.

Chapter 6: Arrays,Vectors, And Hashtables

Figure 6.1: A three-dimensional array in memory. Each rectangle is an array.
Figure 6.2: Java's Hashtable hashes the key into a unique index in an internal table, for storing the value.

Chapter 9: An Object Orientation

Figure 9.1: Encapsulation of methods.
Figure 9.2: An example of a sim-ple classes inheriting from a parent class
Figure 9.3: An inheritance hierarchy tree with multiple inheritance hierarchy tree..
Figure 9.4: VisualAge for Java's class hierarchy browser.

Chapter 10: Exceptions

Figure 10.1: The major child classes of the Java Throwable class.
Figure 10.2: Where to find JDK documentation.
Figure 10.3: The index.html file from the \docs\api directory.
Figure 10.4: After selecting the Tree link, all packages are listed.
Figure 10.5: The class hierarchy for the children of the Exception class.
Figure 10.6: Where to create your own exception classes.

Chapter 11: Threads

Figure 11.1: Synchronous versus asynchronous calls.
Figure 11.2: Threads sharing instance variables.
Figure 11.3: Using the start method to spawn a method asynchronously.
Figure 11.4: Using the start method and the Runnable interface to spawn a method asynchronously.
Figure 11.5: A single repository class and threaded transaction classes.
Figure 11.6: A Java server with multiple clients.
Figure 11.7: Thread time-splicing.
Figure 11.8: A synchronized lock count.
Figure 11.9: A lock queue versus a wait queue. Tn represents thread n.
Figure 11.10: Deadlock.

Chapter 12: User Interface

Figure 12.1: A simple window with a push button.
Figure 12.2: A quick overview of all AWT components.
Figure 12.3: A blank Java frame window.
Figure 12.4: A blank Java dialog window.
Figure 12.5a: BorderLayout without the call to pack.
Figure 12.5b: BorderLayout with the call to pack.
Figure 12.6a: FlowLayout without calling pack.
Figure 12.6b: FlowLayout with a call to pack.
Figure 12.7a: GridLayout without calling pack.
Figure 12.7b: GridLayout with a call to pack.
Figure 12.8: Using GridLayout for a properties sheet.
Figure 12.9: Using GridLayout for evenly sized buttons.
Figure 12.10: Planning for GridBagLayout.
Figure 12.11a: GrigBagLayout without a call to pack.
Figure 12.11b: GrigBagLayout with a call to pack.
Figure 12.12: Using positive weights in GridBagConstraints.
Figure 12.13: Using padding between components.
Figure 12.14. A reusable panel of buttons.
Figure 12.15. A reusable panel of buttons with evenly sized buttons.
Figure 12.16: A reusable prompt panel.
Figure 12.17: Menu bars, menus, and menu items.
Figure 12.18: A menu item with a toggle.
Figure 12.19: A popup menu.
Figure 12.20: Menu shortcuts.
Figure 12.21: Processing ActionEvent for a push button.
Figure 12.22: A simple to-do list application in AWT.
Figure 12.23: An applet running in Netscape.
Figure 12.24: A translated application.
Figure 12.25: The basic Swing components.
Figure 12.26: The Windows look and feel.
Figure 12.27: The to-do list sample redone in Swing.

Chapter 13: Database Access

Figure 13.1: The JDBC frame-work.

Chapter 14: More Java

Figure 14.1: The Java platform.
Figure 14.2: The AWT Open/Save dialog (FileDialog).
Figure 14.3: The Swing Open/Save dialog (JFileChooser).
Figure 14.4: Using VisualAge for Java's BeanInfo page to create bean features.
Figure 14.5: Testing a Java bean in VisualAge for Java's Visual Composition Editor.
Figure 14.6: VisualAge for Java's Workbench window.
Figure 14.7: An HTML page generated by the Servlet in Listing 14.20.
Figure 14.8: An HTML input form.
Figure 14.9: The output of the ProcessInputServlet Servlet.
Figure 14.10: Configuring the IBM HTTP Server service.
Figure 14.11: The main WorkBench window in VisualAge for Java.
Figure 14.12: VisualAge for Java's Create Method SmartGuide.
Figure 14.13: An open package window in VisualAge for Java.
Figure 14.14: VisualAge for Java's edit pane with a breakpoint.
Figure 14.15: VisualAge for Java's debug window.
Figure 14.16: Using the New Application SmartGuide in VisualAge for Java.
Figure 14.17: The Visual Composition Editor in VisualAge for Java.

Appendix A: COBOL For OS/390 Flow Of Control

Figure A.1: Calling between COBOL programs.
Figure A.2: Nested COBOL programs.
Figure A.3a: Creating DLLs.
Figure A.3b: Using DLLs.
Figure A.4: Flow of control.

Appendix B: ILE COBOL Flow Of Control

Figure B.1: A traditional COBOL application.
Figure B.2: Compiling a traditional COBOL program.
Figure B.3: An ILE COBOL application.
Figure B.4: COBOL reuse through service programs.
Figure B.5: Compiling programs and service programs.
Figure B.6: The flow of control in an ILE COBOL application.

Appendix C: Object-Oriented COBOL On OS/390

Figure C.1: Java's object model.
Figure C.2: Anatomy of a COBOL class.
Figure C.3: OO COBOL's object model.

Appendix D: Using Java On OS/390

Figure D.1: CCF Connectors in VisualAge for Java.
Figure D.2: The EAB tools menu.
Figure D.3: The Create Command SmartGuide for a CICS command bean.
Figure D.4: The EAB Command Editor window for CICS.

Appendix F: Software Loading Instructions

Figure F.1: A supplied sample application for managing to-do items.


 Team-Fly

List of Tables

Chapter 2: Java Flow of Control

Table 2.1: Comparing COBOL and Java Anatomies.
Table 2.2: Comparing COBOL Program Calls to Java Method Calls.
Table 2.3: Java Method Modifiers.
Table 2.4: Comparing Java Access Modifiers to COBOL Program Access in DLLs or Service Programs.
Table 2.5: Breakdown of Signature.
Table 2.6: Summary of Analogies between COBOL and Java.

Chapter 3: Java Syntax

Table 3.1: Comment Types in Java.
Table 3.2: JavaDoc 1.1.x Files.
Table 3.3: JavaDoc 1.2.x Files.
Table 3.4: . JavaDoc Tags.
Table 3.5: Reserved Words in Java.
Table 3.6: Fetch Expressions.
Table 3.7: Computational Expressions.
Table 3.8: Binary Arithmetic Operators.
Table 3.9: Binary Operator Examples.
Table 3.10: Remainder and Modulus.
Table 3.11: Assignment Example.
Table 3.12: Contracted Operators in Java.
Table 3.13: The Increment Operator.
Table 3.14: Unary Operators.
Table 3.15: Unary Operator Examples.
Table 3.16: Relation Expressions Example.
Table 3.17: Relational Operators.
Table 3.18: Logical Operators.
Table 3.19: Java Bitwise Operators.
Table 3.20: COBOL Operator Precedence.
Table 3.21: Java Operator Precedence.
Table 3.22: Methods in Java's Math Class.

Chapter 4: Structured Operations and Statements

Table 4.1: Structured Statements in COBOL Versus Java.
Table 4.2: COBOL's IF Statement.
Table 4.3: Using Parentheses around Sub-expressions.
Table 4.4: COBOL's EVALUATE Versus Java's switch Statement.
Table 4.5: COBOL's EVALUATE Versus Java's switch Sub-statements.
Table 4.6: COBOL In-Line Perform Versus Java Loop Statements.
Table 4.7: COBOL Out-of-Line PERFORM Versus Java FOR, WHILE, and DO-WHILE.

Chapter 5: Data Types And Variables

Table 5.1: COBOL Categories for Data Items.
Table 5.2: Java Data Types.
Table 5.3: COBOL Versus Java Data Types.
Table 5.4: Java Modifiers.
Table 5.5: Example Literals.
Table 5.6: Java Escape-Character Sequences.
Table 5.7: COBOL Memory Requirements by Data Type.
Table 5.8: Java Memory Requirements by Data Type.
Table 5.9: Java Casting Requirements by Data Type. (Read left to right.)
Table 5.10: Java Primitive-Data-Type Class Wrappers.

Chapter 6: Arrays,Vectors, And Hashtables

Table 6.1: COBOL Tables Versus Java Arrays.
Table 6.2: Methods in the java.util.Arrays Class.
Table 6.3: Methods in the java.util.Vector Class.
Table 6.4: Methods in the java.util.Hashtable Class.

Chapter 7: String Manipulation

Table 7.1: COBOL Versus Java String-Manipulation Statements.
Table 7.2: A Summary of String Class Methods.

Chapter 8: Date And Time Manipulation

Table 8.1: Date Data Item Intrinsic Functions.
Table 8.2: ILE COBOL Conversion Specifiers for Date Data Items.
Table 8.3: ILE COBOL Conversion Specifiers for Time Data Items.
Table 8.4: ILE COBOL Intrinsic Functions for Date-Time Manipulations.
Table 8.5: Java Support for Date and Time.
Table 8.6: Constants for Extracting Date Parts via the get Method in the GregorianCalendar Class.
Table 8.7: Constants for Extracting Time Parts via the get Method in the GregorianCalendar Class.
Table 8.8: Date Formatting Symbols for the SimpleDateFormat Class.
Table 8.9: Time-Formatting Symbols for the SimpleDateFormat Class.

Chapter 9: An Object Orientation

Table 9.1: Java Modifiers.
Table 9.2: Methods to Be Overridden.

Chapter 12: User Interface

Table 12.1: Green Screen Versus GUI Differences.
Table 12.2: Basic AWT Components.
Table 12.3: How to Use the Basic AWT Components.
Table 12.4: Common Methods Shared by All AWT Components.
Table 12.5: Basic AWT Color Constants in the Color Class of the java.awt Package.
Table 12.6: Color Constants in the SystemColor Class of the java.awt Package.
Table 12.7: Layout Managers in AWT.
Table 12.8: GridBagConstraints Variables.
Table 12.9: Event-Listener Interfaces.
Table 12.10: Common Set and Get Methods for AWT Components.
Table 12.11: Four Methods to Override in Applets.
Table 12.12: Swing Versus AWT.
Table 12.13: Advanced Functions in Swing.

Chapter 13: Database Access

Table 13.1: SQL Error and Warning Codes.
Table 13.2: Static Versus Dynamic SQL.
Table 13.3: Embedded Static Versus Embedded Dynamic Versus CLI APIs.
Table 13.4: Common DDL Statements.
Table 13.5: Common DML Statements.
Table 13.6: The Main Interfaces in JDBC.
Table 13.7: JDBC Driver-Connection Property Values.
Table 13.8: PreparedStatement Set Methods for Marker Substitution.
Table 13.9: get Methods for Retrieving Column Data from ResultSet.
Table 13.10: SQL versus Java Data Types.
Table 13.11: Transaction Isolation Constants from the java.sql.Connection Class.
Table 13.12: ResultSetMetaData Methods.
Table 13.13: DatabaseMetaData Functionality.
Table 13.14: New ResultSet Methods in JDBC 2.0.

Chapter 14: More Java

Table 14.1: JDK 1.1.x Packages.
Table 14.2: New Packages in JDK 1.2.x.
Table 14.3: Interesting JFileChooser Methods.
Table 14.4: An Example of Properties.
Table 14.5: Features of a Java Bean.
Table 14.6: Java Bean Design Thought Process.
Table 14.7: The Features of the Inventory Java Bean.
Table 14.8: Important Servlet Methods (Extending the javax.servlet.http.HttpServlet).
Table 14.9: VisualAge for Java Versions.
Table 14.10: Additional Functionality in VisualAge for Java.

Appendix C: Object-Oriented COBOL On OS/390

Table C.1: Comparing Object Models Between OO COBOL and Java.

Appendix D: Using Java On OS/390

Table D.1: Installation and Ordering Information.
Table D.2: Windows Versus Ishell Commands.
Table D.3: Options for Copying Java Files to OS/390 HFS.
Table D.4: CCF Connectors in VisualAge for Java.
Table D.5: CICS Classes in the com.ibm.cics.server Package.
Table D.6: VisualAge for Java ET/390 Support for OS/390 Programmers.

Appendix E: Using Java On OS/400

Table E.1: OS/400 Java and Related Products Installation Information.
Table E.2: Windows, IFS CL, and QShell Commands.
Table E.3: Windows, IFS CL, and QShell Commands.
Table E.4: AS/400 Toolbox for Java Functions as of V4R4 (V3R2M2).
Table E.5: AS/400 Toolbox for Java Helper Classes.
Table E.6: AS/400 Toolbox for Java Graphical Access Classes as of V4R4 (V3R2M2).
Table E.7: Data Conversion Classes in AS/400 Toolbox for Java.
Table E.8: Input Data Queue Format.
Table E.9: Output Data Queue Format.
Table E.10: Field Description Classes in AS/400 Toolbox for Java.
Table E.11: VisualAge for Java ET/400 Support for OS/400 Programmers.


 Team-Fly
Previous Section

List of Listings

Chapter 1: The World of Java

Listing 1.1: An example of HTML tags.
Listing 1.2: An example of HTML that provides the ability to jump from one location to another.

Chapter 2: Java Flow of Control

Listing 2.1: Using this to qualify an instance variable.
Listing 2.2: Using this dot qualification.
Listing 2.3: Declare and define methods named swap and compare.
Listing 2.4: The archive parameter pointing to the JAR file.
Listing 2.5: Defining methods as static.
Listing 2.6: Classes and methods that act on instance variables in the class.
Listing 2.7: An example of one constructor calling other constructors.
Listing 2.8: A simple COBOL program to display a passed string.
Listing 2.9: The Java equivalent of the COBOL program in Listing 2.8.
Listing 2.10: The "hello world" program with additional functionality.

Chapter 3: Java Syntax

Listing 3.1: A documented class.
Listing 3.2: A summary of Java statement types.
Listing 3.3: An example of using a bitwise AND operation.
Listing 3.4: An example of bitwise OR operators.
Listing 3.5: Using shift operators.
Listing 3.6: Using the Math class in Java.

Chapter 4: Structured Operations and Statements

Listing 4.1: A COBOL example for computing and displaying the factorial of an integer.
Listing 4.2: A Java example for computing and displaying the factorial of an integer.
Listing 4.3: A Java example of a two-dimensional array.
Listing 4.4: An example of COBOL's PERFORM UNTIL statement.
Listing 4.5: The Java equivalent of the COBOL code in Listing 4.4.
Listing 4.6: A COBOL example of a do…until loop structure.
Listing 4.7: A Java example of a do…while structure.
Listing 4.8: Using Java's break statement.
Listing 4.9: Adding the continue statement to the break structure.
Listing 4.10: Using a break statement with the outer and inner dimensions of an array.
Listing 4.11: A COBOL example to return a result back to the caller.
Listing 4.12: Using a return statement to end a method in Java.

Chapter 5: Data Types And Variables

Listing 5.1: An example of Java variables.
Listing 5.2: Accessing class-level variables.
Listing 5.3: Java calculations for pricing.
Listing 5.4: Using the BigDecimal class for rounding.
Listing 5.5: Using multiple types of numbers.
Listing 5.6: Using the Float class to convert a number from a character string to a primitive float.

Chapter 6: Arrays,Vectors, And Hashtables

Listing 6.1: Initializing a runtime array in COBOL.
Listing 6.2: Java code to compute, and store in an array, the factorial for numbers 0 through 9.
Listing 6.3: The revised Java factorial code, using the built-in length variable for the array size.
Listing 6.4: Initializing a two-dimensional compile-time array.
Listing 6.5: Initializing a runtime array in Java.
Listing 6.6: Initializing a three-dimensional array in Java.
Listing 6.7: Initializing a three-dimensional array in COBOL.
Listing 6.8: An example to illustrate that array variables in Java are, in fact, references to array objects.
Listing 6.9: A definition of a table of group items in COBOL.
Listing 6.10: Using classes to define the same table as in Listing 6.9.
Listing 6.11: A Java class containing an array of objects, each of type Department.
Listing 6.12: An example of a search algorithm.
Listing 6.13: A sort method followed by a binarySearch method.
Listing 6.14: A simple vector with three elements.
Listing 6.15: An example of vector logic.
Listing 6.16: An example of the readFromConsole method.
Listing 6.17: A Java class to register and "unregister" names. A vector is used to store the names.
Listing 6.18: A revised Java class to register names. A hashtable is used to store the names, keyed by telephone number.
Listing 6.19: A COBOL program highlighting the differences between passing parameters by value, content, and reference.
Listing 6.20: A Java class highlighting that elements of an array parameter can be changed in the method, but the array reference itself cannot be changed.
Listing 6.21: An example of a swap method in Java.

Chapter 7: String Manipulation

Listing 7.1: An example of the STRING statement in COBOL.
Listing 7.2: An example of string manipulation in Java.
Listing 7.3: An example of a substring operation in COBOL.
Listing 7.4: An example of the substring method in Java.
Listing 7.5: Using a class to define a group item.
Listing 7.6: Searching one string for the occurrence of another in COBOL.
Listing 7.7: Finding the position of a substring within a string.
Listing 7.8: Determining the position of a substring within a string in Java.
Listing 7.9: Trimming blanks in COBOL.
Listing 7.10: Trimming blanks in Java.
Listing 7.11: Determining string length in COBOL.
Listing 7.12: Determining the length of a string in Java.
Listing 7.13: COBOL's INSPECT CONVERTING statement.
Listing 7.14: A Java method that is equivalent to COBOL's INSPECT CONVERTING statement.
Listing 7.15: An alternative to the INSPECT CONVERTING statement in COBOL.
Listing 7.16: Converting case in Java.
Listing 7.17: Checking the validity of input with the class condition test.
Listing 7.18: Validity testing in Java.
Listing 7.19: The differences between length and capacity in the StringBuffer class.
Listing 7.20: Trimming trailing blanks in Java.
Listing 7.21: Trimming leading blanks in Java.
Listing 7.22: COBOL's UNSTRING operation.
Listing 7.23: Extracting a single word from a string in Java.

Chapter 8: Date And Time Manipulation

Listing 8.1: Retrieving elapsed time using method currentTimeMillis.
Listing 8.2: The ElapsedTime class.
Listing 8.3: The Date class from java.util.
Listing 8.4: Using getTime to display a Date object.
Listing 8.5: Examples of value options for the GregorianCalendar class.
Listing 8.6: An example of the before method.
Listing 8.7: Examples of COBOL duration functions.
Listing 8.8: Adding date parts to a date in Java.
Listing 8.9: Displaying date information in COBOL.
Listing 8.10: Displaying date information in Java.
Listing 8.11: Formatting date data items in Java.
Listing 8.12: Date formatting examples, using both symbols and literals in the format.
Listing 8.13: Formatting time data items.
Listing 8.14: Time-formatting examples, using both symbols and literals in t he format.
Listing 8.15: Formatting date and time with country-specific preferences.
Listing 8.16: Examples of short, medium, long, and full formatting of dates and time.
Listing 8.17: Coding for leap year.
Listing 8.18: Coding for different time zones.

Chapter 9: An Object Orientation

Listing 9.1: A parent class that represents an employee, with common information and functions.
Listing 9.2: Extending a class adds additional variable and method to the class.
Listing 9.3: Overriding a method in the parent class.
Listing 9.4: A new version of getBiWeeklyPay.
Listing 9.5: The SalesClerk class with a new constructor.
Listing 9.6: A factory method to instantiate and return an object of the appropriate type.
Listing 9.7: A parent class with a method to compute the area of a shape.
Listing 9.8: A class to compute the area of a circle.
Listing 9.9: A class to compute the area of a square.
Listing 9.10: A class to compute the area of a triangle.
Listing 9.11: A class to test and display the results of the shape-computing classes.
Listing 9.12: Classes with even one abstract method must be declared abstract at the class level.
Listing 9.13: A base class, and its constructor, for tracking billable services.
Listing 9.14: A child class whose constructor takes more parameters than its parent class.
Listing 9.15: An example of a Java interface.
Listing 9.16: An implementation of the print method signature from the Printable interface.
Listing 9.17: A method for searching for an integer value in an integer array.
Listing 9.18: A method for searching for a shape value in a shape array.
Listing 9.19: A method for searching for an employee value in an employee array.
Listing 9.20: A generic array-search method.
Listing 9.21: Using an interface called Enumeration to walk through an array.
Listing 9.22: An example of an enumerate class called Our Department.

Chapter 10: Exceptions

Listing 10.1: Defining a new exception.
Listing 10.2: Throwing an exception.
Listing 10.3: Calling methods that can throw exceptions: try and catch blocks.

Chapter 11: Threads

Listing 11.1: The PrintReport class.
Listing 11.2: The print method from the PrintableEmployee class.
Listing 11.3: The full PrintReport class.
Listing 11.4: The PrintReportThread1 class, to be run asynchronously.
Listing 11.5: Using threads by implementing the Runnable interface.
Listing 11.6: A long-running method, and the required run method that calls it.
Listing 11.7: A long-running method that handles being prematurely stopped by the stop method.
Listing 11.8: An Inventory class that takes orders and adjusts the inventory when stock is low.
Listing 11.9: A typical transaction thread class.
Listing 11.10: A class to test the previous example.
Listing 11.11: The TestInventory class to print out the elapsed time.
Listing 11.12: How to wait for multiple threads to complete.

Chapter 12: User Interface

Listing 12.1: Using the add method of the frame class.
Listing 12.2: A template for a window class In Java AWT. GUI initialization is done in the constructor.
Listing 12.3: An example of a Dialog class.
Listing 12.4: An example of a Frame window class extension.
Listing 12.5: A sample class using the FlowLayout manager.
Listing 12.6: A sample class using the GridLayout manager.
Listing 12.7: A sample class using the GridBagLayout manager.
Listing 12.8: Using Panel instead of Frame or Dialog.
Listing 12.9: Placing a Panel object inside a Frame window.
Listing 12.10: Converting the entire client area to a panel.
Listing 12.11: Placing a client-area panel inside a frame.
Listing 12.12: Defining a menu that contains nested menus, which contain menu items.
Listing 12.13: Defining "toggle-able" menu items.
Listing 12.14: Defining a popup menu, and displaying a popup menu.
Listing 12.15: Adding a message line to the window.
Listing 12.16: Adding code to process the pressing of the List button.
Listing 12.17: A reusable class for processing the window-closing event.
Listing 12.18: The ToDoList class.
Listing 12.19: The ToDoListEntry class.
Listing 12.20: A method to pad and justify an integer data type.
Listing 12.21: A method to verify the padded and justified number from Listing 12.20.
Listing 12.22: A substring-substitution static method class.
Listing 12.23: Converting a Frame window class to an applet class.
Listing 12.24: The HTML source to embed an applet.
Listing 12.25: Writing default locale information to the console.
Listing 12.26: The ResourceBundle subclass containing all translatable strings.
Listing 12.27: The translated ResourceBundle subclass containing translated strings.
Listing 12.28: Converting hard-coded strings to ResourceBundle retrieved strings.
Listing 12.29: A Java window class to show each of the basic Swing components.

Chapter 13: Database Access

Listing 13.1: Starting a Java JDBC class by importing the java.sql package.
Listing 13.2: Registering the JDBC driver.
Listing 13.3: Connecting to the database.
Listing 13.4: How to process result sets in a thread.
Listing 13.5: The database query example.
Listing 13.6: Helper methods for padding strings.
Listing 13.7: TestMetaData constructor to display metadata information.
Listing 13.8: The example from Listing 13.5, redone with SQLJ.

Chapter 14: More Java

Listing 14.1: A Java class containing other subordinate classes.
Listing 14.2: An inner class directly inside the method that uses it.
Listing 14.3: An example that uses an anonymous inner class.
Listing 14.4: The main method of the DeptSerializable class.
Listing 14.5: Re-instantiating an object serialized to disk.
Listing 14.6: Determining the working directory with the System.getProperty ("user.dir") static method call.
Listing 14.7: Setting a working directory in JDK 1.1.x.
Listing 14.8: Listing all files in the current working directory.
Listing 14.9: A name filter class to show only names ending in .java.
Listing 14.10: Displaying an AWT Open dialog.
Listing 14.11: Displaying an Open dialog using Swing.
Listing 14.12: A subset to show only Java files.
Listing 14.13: Retrieving a list of drives on your current system.
Listing 14.14: Creating a directory.
Listing 14.15: Writing lines of text to a flat (stream) file.
Listing 14.16: Reading a flat file.
Listing 14.17: Running native executables from Java.
Listing 14.18: Saving a Profile object to disk.
Listing 14.19: Reading a Profile object from disk.
Listing 14.20: A Servlet class that returns a Web page listing the current directory's contents.
Listing 14.21: The HTML source for a form.
Listing 14.22: A Servlet class to process an HTML form.
Listing 14.23: A JSP that displays a Web page listing the current directory's contents.
Listing 14.24: A Java class to return the current directory's contents.
Listing 14.25: A simplified JavaServer Page that uses a Java class to do the logic.

Appendix C: Object-Oriented COBOL On OS/390

Listing C.1: An OO COBOL class.
Listing C.2: An OO COBOL class with method definitions.

Appendix D: Using Java On OS/390

Listing D.1: Using Java in batch.
Listing D.2: A linked program with a passed integer value.

Appendix E: Using Java On OS/400

Listing E.1: A superclass of ProgramParameter.
Team-Fly Previous Section